未验证 提交 db7be066 编写于 作者: B Bogdan Kobylynskyi 提交者: GitHub

Introduce config for specifying custom-serializable fields #260 (#392)

* [WIP] Introduce config for specifying custom-serializable fields #260

* Add a new config to maven/gradle/sbt #260
上级 496563da
Some breaking changes were introduced in [Release 4.0.0](https://github.com/kobylynskyi/graphql-java-codegen/releases/tag/v4.0.0).
So if you were using version 3.x.x then please follow steps below.
Note: if you are migrating from version 2.x.x, then please also follow [3.0.0 migration guide](migration-to-3.0.0.md) first.
## Migration steps
### 1. Update plugin and library versions
As per plugin description: [Gradle](https://github.com/kobylynskyi/graphql-java-codegen/tree/master/plugins/gradle), [Maven](https://github.com/kobylynskyi/graphql-java-codegen/tree/master/plugins/maven)
### 2. Change GraphQL Resolvers containing non-null GraphQL types to primitive Java types
This effects the following types:
* GraphQL `Int`
* Non-null -> Java `int`
* Nullable -> Java `Integer`
* GraphQL `Float`
* Non-null -> Java `double`
* Nullable -> Java `Double`
* GraphQL `Boolean`
* Non-null -> Java `boolean`
* Nullable -> Java `Boolean`
#### Example
Let's suppose there is a graphql schema:
```graphql
type Query {
diffTypes(intNonNull: Int!,
boolNonNull: Boolean!,
floatNonNull: Float!,
intNullable: Int,
boolNullable: Boolean,
floatNullable: Float): Int!
}
```
And your GraphQL resolver (in version 3.x.x and below) looks like the following:
```java
public class DiffTypesQueryResolverImpl implements DiffTypesQueryResolver {
@Override
public Integer diffTypes(Integer intNonNull,
Boolean boolNonNull,
Double floatNonNull,
Integer intNullable,
Boolean boolNullable,
Double floatNullable) { return null; }
}
```
Now in version 4.0.0 the FeedsQueryResolver interface will be generated **with primitive types** for non-null GraphQL types:
```java
public interface DiffTypesQueryResolver {
int diffTypes(int intNonNull,
boolean boolNonNull,
double floatNonNull,
Integer intNullable,
Boolean boolNullable,
Double floatNullable);
}
```
So you should change your resolver implementation to:
```java
public class DiffTypesQueryResolverImpl implements DiffTypesQueryResolver {
@Override
public int diffTypes(int intNonNull, // should be changed: Integer -> int
boolean boolNonNull, // should be changed: Boolean -> boolean
double floatNonNull, // should be changed: Double -> double
Integer intNullable, // should not be changed because it is nullable
Boolean boolNullable, // should not be changed because it is nullable
Double floatNullable) { // should not be changed because it is nullable
return 0;
}
}
```
### 3. Regenerate the code
Run project build so that GraphQL classes are regenerated and your code compiles.
---
Feel free to ask any questions in [Gitter community](https://gitter.im/graphql-java-codegen/community) or [create an issue](https://github.com/kobylynskyi/graphql-java-codegen/issues) if you discover some problems.
......@@ -85,11 +85,12 @@ public class GraphQLCodegenGradleTask extends DefaultTask implements GraphQLCode
private String responseSuffix;
private String responseProjectionSuffix;
private String parametrizedInputSuffix;
private int responseProjectionMaxDepth = MappingConfigConstants.DEFAULT_RESPONSE_PROJECTION_MAX_DEPTH;
private Set<String> useObjectMapperForRequestSerialization = new HashSet<>();
private final ParentInterfacesConfig parentInterfaces = new ParentInterfacesConfig();
private String jsonConfigurationFile;
private int responseProjectionMaxDepth = MappingConfigConstants.DEFAULT_RESPONSE_PROJECTION_MAX_DEPTH;
public GraphQLCodegenGradleTask() {
setGroup("codegen");
setDescription("Generates Java POJOs and interfaces based on GraphQL schemas");
......@@ -130,17 +131,20 @@ public class GraphQLCodegenGradleTask extends DefaultTask implements GraphQLCode
mappingConfig.setGenerateModelsForRootTypes(generateModelsForRootTypes);
mappingConfig.setFieldsWithResolvers(fieldsWithResolvers != null ? fieldsWithResolvers : new HashSet<>());
mappingConfig.setFieldsWithoutResolvers(fieldsWithoutResolvers != null ? fieldsWithoutResolvers : new HashSet<>());
mappingConfig.setRelayConfig(relayConfig);
mappingConfig.setGenerateClient(generateClient);
mappingConfig.setRequestSuffix(requestSuffix);
mappingConfig.setResponseSuffix(responseSuffix);
mappingConfig.setResponseProjectionSuffix(responseProjectionSuffix);
mappingConfig.setParametrizedInputSuffix(parametrizedInputSuffix);
mappingConfig.setUseObjectMapperForRequestSerialization(useObjectMapperForRequestSerialization != null ? useObjectMapperForRequestSerialization : new HashSet<>());
mappingConfig.setResponseProjectionMaxDepth(responseProjectionMaxDepth);
mappingConfig.setResolverParentInterface(getResolverParentInterface());
mappingConfig.setQueryResolverParentInterface(getQueryResolverParentInterface());
mappingConfig.setMutationResolverParentInterface(getMutationResolverParentInterface());
mappingConfig.setSubscriptionResolverParentInterface(getSubscriptionResolverParentInterface());
mappingConfig.setResponseProjectionMaxDepth(getResponseProjectionMaxDepth());
mappingConfig.setRelayConfig(relayConfig);
new GraphQLCodegen(getActualSchemaPaths(), graphqlQueryIntrospectionResultPath, outputDir, mappingConfig, buildJsonSupplier()).generate();
}
......@@ -581,6 +585,17 @@ public class GraphQLCodegenGradleTask extends DefaultTask implements GraphQLCode
this.fieldsWithoutResolvers = fieldsWithoutResolvers;
}
@Nested
@Optional
@Override
public RelayConfig getRelayConfig() {
return relayConfig;
}
public void relayConfig(Action<? super RelayConfig> action) {
action.execute(relayConfig);
}
@Input
@Optional
@Override
......@@ -636,15 +651,26 @@ public class GraphQLCodegenGradleTask extends DefaultTask implements GraphQLCode
this.parametrizedInputSuffix = parametrizedInputSuffix;
}
@Nested
@Input
@Optional
@Override
public RelayConfig getRelayConfig() {
return relayConfig;
public Set<String> getUseObjectMapperForRequestSerialization() {
return useObjectMapperForRequestSerialization;
}
public void relayConfig(Action<? super RelayConfig> action) {
action.execute(relayConfig);
public void setUseObjectMapperForRequestSerialization(Set<String> useObjectMapperForRequestSerialization) {
this.useObjectMapperForRequestSerialization = useObjectMapperForRequestSerialization;
}
@Input
@Optional
@Override
public Integer getResponseProjectionMaxDepth() {
return responseProjectionMaxDepth;
}
public void setResponseProjectionMaxDepth(int responseProjectionMaxDepth) {
this.responseProjectionMaxDepth = responseProjectionMaxDepth;
}
@Nested
......@@ -691,15 +717,4 @@ public class GraphQLCodegenGradleTask extends DefaultTask implements GraphQLCode
this.jsonConfigurationFile = jsonConfigurationFile;
}
@Input
@Optional
@Override
public Integer getResponseProjectionMaxDepth() {
return responseProjectionMaxDepth;
}
public void setResponseProjectionMaxDepth(int responseProjectionMaxDepth) {
this.responseProjectionMaxDepth = responseProjectionMaxDepth;
}
}
......@@ -77,9 +77,6 @@ public class GraphQLCodegenMojo extends AbstractMojo implements GraphQLCodegenCo
@Parameter(defaultValue = MappingConfigConstants.DEFAULT_TO_STRING_STRING)
private boolean generateToString;
@Parameter(defaultValue = MappingConfigConstants.DEFAULT_RESPONSE_PROJECTION_MAX_DEPTH_STRING)
private int responseProjectionMaxDepth;
@Parameter
private String apiPackageName;
......@@ -149,6 +146,9 @@ public class GraphQLCodegenMojo extends AbstractMojo implements GraphQLCodegenCo
@Parameter
private String[] fieldsWithoutResolvers;
@Parameter
private RelayConfig relayConfig = new RelayConfig();
@Parameter(defaultValue = MappingConfigConstants.DEFAULT_GENERATE_CLIENT_STRING)
private boolean generateClient;
......@@ -165,14 +165,17 @@ public class GraphQLCodegenMojo extends AbstractMojo implements GraphQLCodegenCo
private String parametrizedInputSuffix;
@Parameter
private RelayConfig relayConfig = new RelayConfig();
private String[] useObjectMapperForRequestSerialization;
@Parameter
private String jsonConfigurationFile;
@Parameter(defaultValue = MappingConfigConstants.DEFAULT_RESPONSE_PROJECTION_MAX_DEPTH_STRING)
private int responseProjectionMaxDepth;
@Parameter
private ParentInterfacesConfig parentInterfaces = new ParentInterfacesConfig();
@Parameter
private String jsonConfigurationFile;
/**
* The project being built.
*/
......@@ -216,17 +219,20 @@ public class GraphQLCodegenMojo extends AbstractMojo implements GraphQLCodegenCo
mappingConfig.setGenerateApisWithThrowsException(generateApisWithThrowsException);
mappingConfig.setFieldsWithResolvers(mapToHashSet(fieldsWithResolvers));
mappingConfig.setFieldsWithoutResolvers(mapToHashSet(fieldsWithoutResolvers));
mappingConfig.setRelayConfig(relayConfig);
mappingConfig.setGenerateClient(generateClient);
mappingConfig.setRequestSuffix(requestSuffix);
mappingConfig.setResponseSuffix(responseSuffix);
mappingConfig.setResponseProjectionSuffix(responseProjectionSuffix);
mappingConfig.setParametrizedInputSuffix(parametrizedInputSuffix);
mappingConfig.setResponseProjectionMaxDepth(responseProjectionMaxDepth);
mappingConfig.setUseObjectMapperForRequestSerialization(mapToHashSet(useObjectMapperForRequestSerialization));
mappingConfig.setResolverParentInterface(getResolverParentInterface());
mappingConfig.setQueryResolverParentInterface(getQueryResolverParentInterface());
mappingConfig.setMutationResolverParentInterface(getMutationResolverParentInterface());
mappingConfig.setSubscriptionResolverParentInterface(getSubscriptionResolverParentInterface());
mappingConfig.setResponseProjectionMaxDepth(getResponseProjectionMaxDepth());
mappingConfig.setRelayConfig(relayConfig);
MappingConfigSupplier mappingConfigSupplier = buildJsonSupplier(jsonConfigurationFile);
......@@ -457,6 +463,11 @@ public class GraphQLCodegenMojo extends AbstractMojo implements GraphQLCodegenCo
return mapToHashSet(fieldsWithoutResolvers);
}
@Override
public Integer getResponseProjectionMaxDepth() {
return responseProjectionMaxDepth;
}
@Override
public Boolean getGenerateClient() {
return generateClient;
......@@ -482,6 +493,11 @@ public class GraphQLCodegenMojo extends AbstractMojo implements GraphQLCodegenCo
return parametrizedInputSuffix;
}
@Override
public Set<String> getUseObjectMapperForRequestSerialization() {
return mapToHashSet(useObjectMapperForRequestSerialization);
}
@Override
public String getQueryResolverParentInterface() {
return parentInterfaces.getQueryResolver();
......@@ -502,11 +518,6 @@ public class GraphQLCodegenMojo extends AbstractMojo implements GraphQLCodegenCo
return parentInterfaces.getResolver();
}
@Override
public Integer getResponseProjectionMaxDepth() {
return responseProjectionMaxDepth;
}
public ParentInterfacesConfig getParentInterfaces() {
return parentInterfaces;
}
......
......@@ -76,6 +76,8 @@ trait GraphQLCodegenKeys {
val parametrizedInputSuffix = settingKey[String]("parametrizedInputSuffix")
val useObjectMapperForRequestSerialization = settingKey[util.Set[String]]("useObjectMapperForRequestSerialization")
val jsonConfigurationFile = settingKey[Option[String]]("jsonConfigurationFile")
val parentInterfaces = settingKey[ParentInterfacesConfig]("parentInterfaces")
......
......@@ -80,6 +80,7 @@ class GraphQLCodegenPlugin(configuration: Configuration, private[codegen] val co
responseSuffix := MappingConfigConstants.DEFAULT_RESPONSE_SUFFIX,
responseProjectionSuffix := MappingConfigConstants.DEFAULT_RESPONSE_PROJECTION_SUFFIX,
parametrizedInputSuffix := MappingConfigConstants.DEFAULT_PARAMETRIZED_INPUT_SUFFIX,
useObjectMapperForRequestSerialization := new util.HashSet[String](),
typeResolverPrefix := None,
typeResolverSuffix := MappingConfigConstants.DEFAULT_RESOLVER_SUFFIX,
subscriptionReturnType := None,
......@@ -148,6 +149,7 @@ class GraphQLCodegenPlugin(configuration: Configuration, private[codegen] val co
mappingConfig.setResponseSuffix((responseSuffix in GraphQLCodegenConfig).value)
mappingConfig.setResponseProjectionSuffix((responseProjectionSuffix in GraphQLCodegenConfig).value)
mappingConfig.setParametrizedInputSuffix((parametrizedInputSuffix in GraphQLCodegenConfig).value)
mappingConfig.setUseObjectMapperForRequestSerialization((useObjectMapperForRequestSerialization in GraphQLCodegenConfig).value)
mappingConfig.setResolverParentInterface((parentInterfaces in GraphQLCodegenConfig).value.resolver)
mappingConfig.setQueryResolverParentInterface((parentInterfaces in GraphQLCodegenConfig).value.queryResolver)
mappingConfig.setMutationResolverParentInterface((parentInterfaces in GraphQLCodegenConfig).value.mutationResolver)
......
......@@ -65,14 +65,16 @@ public class FieldDefinitionToParameterMapper {
*/
private static ParameterDefinition mapField(MappingContext mappingContext, ExtendedFieldDefinition fieldDef,
String parentTypeName) {
NamedDefinition namedDefinition = GraphqlTypeToJavaTypeMapper.getJavaType(mappingContext, fieldDef.getType(), fieldDef.getName(), parentTypeName);
ParameterDefinition parameter = new ParameterDefinition();
parameter.setName(MapperUtils.capitalizeIfRestricted(fieldDef.getName()));
parameter.setOriginalName(fieldDef.getName());
NamedDefinition type = GraphqlTypeToJavaTypeMapper.getJavaType(mappingContext, fieldDef.getType(), fieldDef.getName(), parentTypeName);
parameter.setType(GraphqlTypeToJavaTypeMapper.getTypeConsideringPrimitive(mappingContext, type));
parameter.setType(GraphqlTypeToJavaTypeMapper.getTypeConsideringPrimitive(mappingContext, namedDefinition));
parameter.setAnnotations(GraphqlTypeToJavaTypeMapper.getAnnotations(mappingContext, fieldDef.getType(), fieldDef, parentTypeName, false));
parameter.setJavaDoc(fieldDef.getJavaDoc());
parameter.setDeprecated(fieldDef.isDeprecated());
parameter.setSerializeUsingObjectMapper(namedDefinition.isSerializeUsingObjectMapper());
return parameter;
}
......
......@@ -168,7 +168,7 @@ public class FieldDefinitionsToResolverDataModelMapper {
if (!Utils.isGraphqlOperation(parentTypeName)) {
String parentObjectParamType = GraphqlTypeToJavaTypeMapper.getJavaType(mappingContext, new TypeName(parentTypeName));
String parentObjectParamName = MapperUtils.capitalizeIfRestricted(Utils.uncapitalize(parentObjectParamType));
parameters.add(new ParameterDefinition(parentObjectParamType, parentObjectParamName, parentObjectParamName, null, emptyList(), emptyList(), resolvedField.isDeprecated()));
parameters.add(new ParameterDefinition(parentObjectParamType, parentObjectParamName, parentObjectParamName, null, emptyList(), emptyList(), resolvedField.isDeprecated(), false));
}
// 2. Next parameters are input values
......
......@@ -128,8 +128,10 @@ public class GraphqlTypeToJavaTypeMapper {
private static NamedDefinition getJavaType(MappingContext mappingContext, String graphQLType, String name,
String parentTypeName, boolean mandatory, boolean collection) {
Map<String, String> customTypesMapping = mappingContext.getCustomTypesMapping();
Set<String> serializeFieldsUsingObjectMapper = mappingContext.getUseObjectMapperForRequestSerialization();
String javaTypeName;
boolean primitiveCanBeUsed = !collection;
boolean serializeUsingObjectMapper = false;
if (name != null && parentTypeName != null && customTypesMapping.containsKey(parentTypeName + "." + name)) {
javaTypeName = customTypesMapping.get(parentTypeName + "." + name);
primitiveCanBeUsed = false;
......@@ -138,8 +140,14 @@ public class GraphqlTypeToJavaTypeMapper {
} else {
javaTypeName = MapperUtils.getModelClassNameWithPrefixAndSuffix(mappingContext, graphQLType);
}
if (serializeFieldsUsingObjectMapper.contains(graphQLType) ||
(name != null && parentTypeName != null &&
serializeFieldsUsingObjectMapper.contains(parentTypeName + "." + name))) {
serializeUsingObjectMapper = true;
}
return new NamedDefinition(javaTypeName, graphQLType, mappingContext.getInterfacesName().contains(graphQLType),
mandatory, primitiveCanBeUsed);
mandatory, primitiveCanBeUsed, serializeUsingObjectMapper);
}
/**
......
......@@ -44,14 +44,16 @@ public class InputValueDefinitionToParameterMapper {
* @return Freemarker-understandable format of parameter (field)
*/
private static ParameterDefinition map(MappingContext mappingContext, InputValueDefinition inputValueDefinition, String parentTypeName) {
NamedDefinition namedDefinition = GraphqlTypeToJavaTypeMapper.getJavaType(mappingContext, inputValueDefinition.getType(), inputValueDefinition.getName(), parentTypeName);
ParameterDefinition parameter = new ParameterDefinition();
parameter.setName(MapperUtils.capitalizeIfRestricted(inputValueDefinition.getName()));
parameter.setOriginalName(inputValueDefinition.getName());
NamedDefinition type = GraphqlTypeToJavaTypeMapper.getJavaType(mappingContext, inputValueDefinition.getType(), inputValueDefinition.getName(), parentTypeName);
parameter.setType(GraphqlTypeToJavaTypeMapper.getTypeConsideringPrimitive(mappingContext, type));
parameter.setType(GraphqlTypeToJavaTypeMapper.getTypeConsideringPrimitive(mappingContext, namedDefinition));
parameter.setDefaultValue(ValueMapper.map(mappingContext, inputValueDefinition.getDefaultValue(), inputValueDefinition.getType()));
parameter.setAnnotations(GraphqlTypeToJavaTypeMapper.getAnnotations(mappingContext, inputValueDefinition.getType(), inputValueDefinition, parentTypeName, false));
parameter.setDeprecated(isDeprecated(inputValueDefinition));
parameter.setSerializeUsingObjectMapper(namedDefinition.isSerializeUsingObjectMapper());
return parameter;
}
......
......@@ -13,11 +13,16 @@ public interface GraphQLCodegenConfiguration {
* Can be used to supply custom mappings for scalars.
* <p>
* Supports:
* * Map of (GraphqlObjectName.fieldName) to (JavaType)
* * Map of (GraphqlType) to (JavaType)
* <ul>
* <li>Map of (GraphqlObjectName.fieldName) to (JavaType)</li>
* <li>Map of (GraphqlType) to (JavaType)</li>
* </ul>
* <p>
* e.g.: DateTime --- String
* e.g.: Price.amount --- java.math.BigDecimal
* E.g.:
* <ul>
* <li>{@code DateTime --- String}</li>
* <li>{@code Price.amount --- java.math.BigDecimal}</li>
* </ul>
*
* @return mappings from GraphqlType to JavaType
*/
......@@ -27,10 +32,13 @@ public interface GraphQLCodegenConfiguration {
* Can be used to supply custom annotations (serializers) for scalars.
* <p>
* Supports:
* * Map of (GraphqlObjectName.fieldName) to (JavaAnnotation)
* * Map of (GraphqlType) to (JavaAnnotation)
* <ul>
* <li>Map of (GraphqlObjectName.fieldName) to (JavaAnnotation)</li>
* <li>Map of (GraphqlType) to (JavaAnnotation)</li>
* </ul>
* <p>
* e.g.: EpochMillis --- @com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = com.example.json.EpochMillisScalarDeserializer.class)
* E.g.:
* {@code EpochMillis --- @com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = com.example.json.EpochMillisScalarDeserializer.class)}
*
* @return mappings from GraphqlType to JavaAnnotation
*/
......@@ -45,7 +53,7 @@ public interface GraphQLCodegenConfiguration {
* <p>
* schema: directive <code>@auth (roles: [String])</code>
* <p>
* directiveAnnotationsMapping: auth --- @org.springframework.security.access.annotation.Secured({{roles}})
* {@code directiveAnnotationsMapping: auth --- @org.springframework.security.access.annotation.Secured({{roles}})}
*
* @return mappings from GraphQL directives to Java annotations.
*/
......@@ -144,7 +152,7 @@ public interface GraphQLCodegenConfiguration {
/**
* Return type for api methods (query / subscription)
* For example: `reactor.core.publisher.Mono`
* For example: <i>reactor.core.publisher.Mono</i>
*
* @return Return type for api methods (query / subscription)
*/
......@@ -152,7 +160,7 @@ public interface GraphQLCodegenConfiguration {
/**
* Return type for api methods (query / subscription) that return list values
* For example: `reactor.core.publisher.Flux`
* For example: <i>reactor.core.publisher.Flux</i>
*
* @return Return type for api methods (query / subscription) that return list values
*/
......@@ -160,7 +168,7 @@ public interface GraphQLCodegenConfiguration {
/**
* Return type for subscription methods.
* For example: `org.reactivestreams.Publisher`, `io.reactivex.Observable`, etc.
* For example: <i>org.reactivestreams.Publisher</i>, <i>io.reactivex.Observable</i>, etc.
*
* @return Return type for subscription methods
*/
......@@ -250,25 +258,38 @@ public interface GraphQLCodegenConfiguration {
RelayConfig getRelayConfig();
/**
* Fields that require Resolvers should be defined here in format: TypeName, TypeName.fieldName, @directive
* If just type is specified, then all fields of this type will have resolvers
* Fields that require Resolvers.
* <p>
* E.g.: "Person.friends"
* E.g.: "Person"
* E.g.: "@customResolver"
* Values should be defined here in format: TypeName, TypeName.fieldName, @directive
* <p>
* If just type is specified, then all fields of this type will have resolvers.
* <p>
* E.g.:
* <ul>
* <li>{@code Person}</li>
* <li>{@code Person.friends}</li>
* <li>{@code @customResolver}</li>
* </ul>
*
* @return Set of types and fields that should have Resolver interfaces.
*/
Set<String> getFieldsWithResolvers();
/**
* Fields that DO NOT require Resolvers should be defined here in format: TypeName, TypeName.fieldName, @directive
* If just type is specified, then all fields of this type will NOT have resolvers
* Can be used in conjunction with "generateExtensionFieldsResolvers"
* Fields that DO NOT require Resolvers.
* <p>
* Values should be defined here in format: TypeName, TypeName.fieldName, @directive
* <p>
* E.g.: "Person.friends"
* E.g.: "Person"
* E.g.: "@noResolver"
* If just type is specified, then all fields of this type will NOT have resolvers.
* <p>
* Can be used in conjunction with {@code generateExtensionFieldsResolvers}
* <p>
* E.g.:
* <ul>
* <li>{@code Person}</li>
* <li>{@code Person.friends}</li>
* <li>{@code @customResolver}</li>
* </ul>
*
* @return Set of types and fields that should NOT have Resolver interfaces.
*/
......@@ -346,10 +367,29 @@ public interface GraphQLCodegenConfiguration {
String getResolverParentInterface();
/**
* Interface that will be limit depth when `all$` invoke which has subProjections
* Limit depth when `all$` invoke which has subProjections
*
* @return limit depth when the projection is constructed automatically
*/
Integer getResponseProjectionMaxDepth();
/**
* Fields that require serialization using
* {@link com.fasterxml.jackson.databind.ObjectMapper#writeValueAsString(Object)}
* <p>
* Values should be defined here in format: <i>GraphqlObjectName.fieldName</i> or <i>GraphqlTypeName</i>
* <p>
* If just type is specified, then all fields of this type will be serialized using ObjectMapper.
* <p>
* E.g.:
* <ul>
* <li>{@code Person.createdDateTime}</li>
* <li>{@code ZonedDateTime}</li>
* </ul>
*
* @return Set of types and fields that should be serialized using
* {@link com.fasterxml.jackson.databind.ObjectMapper#writeValueAsString(Object)}
*/
Set<String> getUseObjectMapperForRequestSerialization();
}
......@@ -70,16 +70,7 @@ public class MappingConfig implements GraphQLCodegenConfiguration, Combinable<Ma
private String responseProjectionSuffix;
private String parametrizedInputSuffix;
private Integer responseProjectionMaxDepth;
@Override
public Integer getResponseProjectionMaxDepth() {
return responseProjectionMaxDepth;
}
public void setResponseProjectionMaxDepth(Integer responseProjectionMaxDepth) {
this.responseProjectionMaxDepth = responseProjectionMaxDepth;
}
private Set<String> useObjectMapperForRequestSerialization = new HashSet<>();
@Override
public void combine(MappingConfig source) {
......@@ -129,6 +120,7 @@ public class MappingConfig implements GraphQLCodegenConfiguration, Combinable<Ma
customAnnotationsMapping = combineMap(customAnnotationsMapping, source.customAnnotationsMapping);
directiveAnnotationsMapping = combineMap(directiveAnnotationsMapping, source.directiveAnnotationsMapping);
responseProjectionMaxDepth = getValueOrDefaultToThis(source, GraphQLCodegenConfiguration::getResponseProjectionMaxDepth);
useObjectMapperForRequestSerialization = combineSet(useObjectMapperForRequestSerialization, source.useObjectMapperForRequestSerialization);
}
private static <T> Map<String, T> combineMap(Map<String, T> thisMap, Map<String, T> otherMap) {
......@@ -554,4 +546,22 @@ public class MappingConfig implements GraphQLCodegenConfiguration, Combinable<Ma
public void setParametrizedInputSuffix(String parametrizedInputSuffix) {
this.parametrizedInputSuffix = parametrizedInputSuffix;
}
@Override
public Integer getResponseProjectionMaxDepth() {
return responseProjectionMaxDepth;
}
public void setResponseProjectionMaxDepth(Integer responseProjectionMaxDepth) {
this.responseProjectionMaxDepth = responseProjectionMaxDepth;
}
@Override
public Set<String> getUseObjectMapperForRequestSerialization() {
return useObjectMapperForRequestSerialization;
}
public void setUseObjectMapperForRequestSerialization(Set<String> useObjectMapperForRequestSerialization) {
this.useObjectMapperForRequestSerialization = useObjectMapperForRequestSerialization;
}
}
......@@ -26,11 +26,6 @@ public class MappingContext implements GraphQLCodegenConfiguration {
this.generatedInformation = generatedInformation;
}
@Override
public Integer getResponseProjectionMaxDepth() {
return config.getResponseProjectionMaxDepth();
}
@Override
public Map<String, String> getCustomTypesMapping() {
return config.getCustomTypesMapping();
......@@ -241,6 +236,16 @@ public class MappingContext implements GraphQLCodegenConfiguration {
return config.getResolverParentInterface();
}
@Override
public Integer getResponseProjectionMaxDepth() {
return config.getResponseProjectionMaxDepth();
}
@Override
public Set<String> getUseObjectMapperForRequestSerialization() {
return config.getUseObjectMapperForRequestSerialization();
}
public ExtendedDocument getDocument() {
return document;
}
......
......@@ -7,14 +7,17 @@ public class NamedDefinition {
private boolean isInterface;
private boolean mandatory;
private boolean primitiveCanBeUsed;
private boolean serializeUsingObjectMapper;
public NamedDefinition(String javaName, String graphqlTypeName,
boolean isInterface, boolean mandatory, boolean primitiveCanBeUsed) {
boolean isInterface, boolean mandatory,
boolean primitiveCanBeUsed, boolean serializeUsingObjectMapper) {
this.javaName = javaName;
this.graphqlTypeName = graphqlTypeName;
this.isInterface = isInterface;
this.mandatory = mandatory;
this.primitiveCanBeUsed = primitiveCanBeUsed;
this.serializeUsingObjectMapper = serializeUsingObjectMapper;
}
public String getJavaName() {
......@@ -57,4 +60,11 @@ public class NamedDefinition {
this.primitiveCanBeUsed = primitiveCanBeUsed;
}
public boolean isSerializeUsingObjectMapper() {
return serializeUsingObjectMapper;
}
public void setSerializeUsingObjectMapper(boolean serializeUsingObjectMapper) {
this.serializeUsingObjectMapper = serializeUsingObjectMapper;
}
}
......@@ -15,7 +15,7 @@ import static java.util.Collections.emptyList;
public class ParameterDefinition {
public static final ParameterDefinition DATA_FETCHING_ENVIRONMENT = new ParameterDefinition(
DataFetchingEnvironment.class.getName(), "env", "env", null, emptyList(), emptyList(), false);
DataFetchingEnvironment.class.getName(), "env", "env", null, emptyList(), emptyList(), false, false);
private String type;
/**
......@@ -30,12 +30,14 @@ public class ParameterDefinition {
private List<String> annotations = new ArrayList<>();
private List<String> javaDoc = new ArrayList<>();
private boolean deprecated;
private boolean serializeUsingObjectMapper;
public ParameterDefinition() {
}
public ParameterDefinition(String type, String name, String originalName, String defaultValue,
List<String> annotations, List<String> javaDoc, boolean deprecated) {
List<String> annotations, List<String> javaDoc,
boolean deprecated, boolean serializeUsingObjectMapper) {
this.type = type;
this.name = name;
this.originalName = originalName;
......@@ -43,6 +45,7 @@ public class ParameterDefinition {
this.annotations = annotations;
this.javaDoc = javaDoc;
this.deprecated = deprecated;
this.serializeUsingObjectMapper = serializeUsingObjectMapper;
}
public String getType() {
......@@ -100,4 +103,13 @@ public class ParameterDefinition {
public void setDeprecated(boolean deprecated) {
this.deprecated = deprecated;
}
public boolean isSerializeUsingObjectMapper() {
return serializeUsingObjectMapper;
}
public void setSerializeUsingObjectMapper(boolean serializeUsingObjectMapper) {
this.serializeUsingObjectMapper = serializeUsingObjectMapper;
}
}
package com.kobylynskyi.graphql.codegen.model.graphql;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.kobylynskyi.graphql.codegen.utils.Utils;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.StringJoiner;
public class GraphQLRequestSerializer {
public static final ObjectMapper OBJECT_MAPPER = Utils.OBJECT_MAPPER;
private GraphQLRequestSerializer() {
}
......@@ -111,22 +114,26 @@ public class GraphQLRequestSerializer {
private static String jsonQuery(String queryString) {
ObjectNode objectNode = Utils.OBJECT_MAPPER.createObjectNode();
objectNode.put("query", queryString);
try {
return Utils.OBJECT_MAPPER.writeValueAsString(objectNode);
} catch (JsonProcessingException e) {
throw new UnableToBuildJsonQueryException(e);
}
return objectMapperWriteValueAsString(objectNode);
}
public static String getEntry(Object input) {
return getEntry(input, false);
}
public static String getEntry(Object input, boolean useObjectMapper) {
if (input == null) {
return null;
}
if (input instanceof Collection<?>) {
Collection<?> inputCollection = (Collection<?>) input;
return inputCollection.stream()
.map(GraphQLRequestSerializer::getEntry)
.collect(Collectors.joining(", ", "[ ", " ]"));
StringJoiner joiner = new StringJoiner(", ", "[ ", " ]");
for (Object collectionEntry : (Collection<?>) input) {
joiner.add(getEntry(collectionEntry, useObjectMapper));
}
return joiner.toString();
}
if (useObjectMapper) {
return objectMapperWriteValueAsString(input);
}
if (input instanceof Enum<?>) {
return input.toString();
......@@ -137,6 +144,14 @@ public class GraphQLRequestSerializer {
}
}
public static String objectMapperWriteValueAsString(Object input) {
try {
return OBJECT_MAPPER.writeValueAsString(input);
} catch (JsonProcessingException e) {
throw new UnableToBuildJsonQueryException(e);
}
}
/**
* Encodes the value as a JSON string according to http://json.org/ rules
*
......
......@@ -128,14 +128,14 @@ public class ${className} implements java.io.Serializable<#if implements?has_con
<#list fields as field>
<#if MapperUtil.isJavaPrimitive(field.type)>
<#if toStringForRequest>
joiner.add("${field.originalName}: " + GraphQLRequestSerializer.getEntry(${field.name}));
joiner.add("${field.originalName}: " + GraphQLRequestSerializer.getEntry(${field.name}<#if field.serializeUsingObjectMapper>, true</#if>));
<#else>
joiner.add("${field.originalName}: " + ${field.name});
</#if>
<#else>
if (${field.name} != null) {
<#if toStringForRequest>
joiner.add("${field.originalName}: " + GraphQLRequestSerializer.getEntry(${field.name}));
joiner.add("${field.originalName}: " + GraphQLRequestSerializer.getEntry(${field.name}<#if field.serializeUsingObjectMapper>, true</#if>));
<#else>
<#if field.type == "String">
joiner.add("${field.originalName}: \"" + ${field.name} + "\"");
......
......@@ -178,4 +178,30 @@ class GraphQLCodegenRequestTest {
getFileByName(files, "EventPropertyChildParametrizedInput.java"));
}
@Test
void generate_UseObjectMapperToSerializeFields_Type() throws Exception {
mappingConfig.putCustomTypeMappingIfAbsent("DateTime", "java.time.ZonedDateTime");
mappingConfig.setUseObjectMapperForRequestSerialization(singleton("DateTime"));
new GraphQLCodegen(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/request/Event_useObjectMapperForRequestSerialization.java.txt"),
getFileByName(files, "Event.java"));
}
@Test
void generate_UseObjectMapperToSerializeFields_Field() throws Exception {
mappingConfig.putCustomTypeMappingIfAbsent("Event.createdDateTime", "java.time.ZonedDateTime");
mappingConfig.setUseObjectMapperForRequestSerialization(singleton("DateTime"));
new GraphQLCodegen(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/request/Event_useObjectMapperForRequestSerialization.java.txt"),
getFileByName(files, "Event.java"));
}
}
\ No newline at end of file
package com.kobylynskyi.graphql.codegen.model.graphql;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.kobylynskyi.graphql.codegen.model.graphql.data.DateInput;
import com.kobylynskyi.graphql.codegen.model.graphql.data.EventPropertyChildParametrizedInput;
import com.kobylynskyi.graphql.codegen.model.graphql.data.EventPropertyParentParametrizedInput;
import com.kobylynskyi.graphql.codegen.model.graphql.data.EventPropertyResponseProjection;
......@@ -9,15 +11,18 @@ import com.kobylynskyi.graphql.codegen.model.graphql.data.EventsByIdsQueryReques
import com.kobylynskyi.graphql.codegen.model.graphql.data.IssueResponseProjection;
import com.kobylynskyi.graphql.codegen.model.graphql.data.OrganizationResponseProjection;
import com.kobylynskyi.graphql.codegen.model.graphql.data.Status;
import com.kobylynskyi.graphql.codegen.model.graphql.data.UpdateDateMutationRequest;
import com.kobylynskyi.graphql.codegen.model.graphql.data.UpdateIssueInput;
import com.kobylynskyi.graphql.codegen.model.graphql.data.UpdateIssueMutationRequest;
import com.kobylynskyi.graphql.codegen.model.graphql.data.UpdateIssuePayloadResponseProjection;
import com.kobylynskyi.graphql.codegen.model.graphql.data.UpdateNodeUnionResponseProjection;
import com.kobylynskyi.graphql.codegen.model.graphql.data.VersionQueryRequest;
import com.kobylynskyi.graphql.codegen.model.graphql.data.ZonedDateTimeSerializer;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.function.Function;
import java.util.stream.Stream;
......@@ -242,6 +247,24 @@ class GraphQLRequestSerializerTest {
assertEquals(expectedQueryDecorator.apply(expectedQueryStr), serializedQuery);
}
@ParameterizedTest(name = "{0}")
@MethodSource("provideAllSerializers")
void serialize_UseObjectMapperForSomeFields(String name, Function<GraphQLRequest, String> serializer, Function<String, String> expectedQueryDecorator) {
GraphQLRequestSerializer.OBJECT_MAPPER.registerModule(
new SimpleModule().addSerializer(new ZonedDateTimeSerializer()));
UpdateDateMutationRequest updateDateMutationRequest = new UpdateDateMutationRequest();
DateInput input = new DateInput();
input.setDateTime(ZonedDateTime.parse("2020-07-30T22:17:17.884-05:00[America/Chicago]"));
updateDateMutationRequest.setInput(input);
GraphQLRequest graphQLRequest = new GraphQLRequest(updateDateMutationRequest);
String serializedQuery = serializer.apply(graphQLRequest).replaceAll(" +", " ").trim();
String expectedQueryStr = "mutation { updateDate(input: { " +
"dateTime: \"2020-07-31T03:17:17.884Z\" }) }";
assertEquals(expectedQueryDecorator.apply(expectedQueryStr), serializedQuery);
}
@ParameterizedTest(name = "{0}")
@MethodSource("provideAllSerializers")
void serialize_complexRequest(String name, Function<GraphQLRequest, String> serializer, Function<String, String> expectedQueryDecorator) {
......
package com.kobylynskyi.graphql.codegen.model.graphql.data;
import com.kobylynskyi.graphql.codegen.model.graphql.GraphQLRequestSerializer;
import java.time.ZonedDateTime;
import java.util.StringJoiner;
public class DateInput {
private ZonedDateTime dateTime;
public ZonedDateTime getDateTime() {
return dateTime;
}
public void setDateTime(ZonedDateTime dateTime) {
this.dateTime = dateTime;
}
@Override
public String toString() {
StringJoiner joiner = new StringJoiner(", ", "{ ", " }");
if (dateTime != null) {
joiner.add("dateTime: " + GraphQLRequestSerializer.getEntry(dateTime, true));
}
return joiner.toString();
}
}
package com.kobylynskyi.graphql.codegen.model.graphql.data;
import com.kobylynskyi.graphql.codegen.model.graphql.GraphQLOperation;
import com.kobylynskyi.graphql.codegen.model.graphql.GraphQLOperationRequest;
import java.util.LinkedHashMap;
import java.util.Map;
public class UpdateDateMutationRequest implements GraphQLOperationRequest {
private static final GraphQLOperation OPERATION_TYPE = GraphQLOperation.MUTATION;
private static final String OPERATION_NAME = "updateDate";
private String alias;
private Map<String, Object> input = new LinkedHashMap<>();
public UpdateDateMutationRequest() {
}
public UpdateDateMutationRequest(String alias) {
this.alias = alias;
}
public void setInput(DateInput input) {
this.input.put("input", input);
}
@Override
public GraphQLOperation getOperationType() {
return OPERATION_TYPE;
}
@Override
public String getOperationName() {
return OPERATION_NAME;
}
@Override
public String getAlias() {
return alias;
}
@Override
public Map<String, Object> getInput() {
return input;
}
}
package com.kobylynskyi.graphql.codegen.model.graphql.data;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public class ZonedDateTimeSerializer extends JsonSerializer<ZonedDateTime> {
@Override
public void serialize(ZonedDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
ZonedDateTime utcDateTime = value.withZoneSameInstant(ZoneId.of("UTC"));
gen.writeString(DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(utcDateTime));
}
@Override
public Class<ZonedDateTime> handledType() {
return ZonedDateTime.class;
}
}
\ No newline at end of file
package com.github.graphql;
import com.kobylynskyi.graphql.codegen.model.graphql.GraphQLRequestSerializer;
import java.util.Objects;
import java.util.StringJoiner;
/**
* An event that describes a thing that happens
*/
@javax.annotation.Generated(
value = "com.kobylynskyi.graphql.codegen.GraphQLCodegen",
date = "2020-12-31T23:59:59-0500"
)
public class Event implements java.io.Serializable {
private String id;
private String categoryId;
private java.util.List<EventProperty> properties;
private EventStatus status;
private String createdBy;
private java.time.ZonedDateTime createdDateTime;
private Boolean active;
private Integer rating;
public Event() {
}
public Event(String id, String categoryId, java.util.List<EventProperty> properties, EventStatus status, String createdBy, java.time.ZonedDateTime createdDateTime, Boolean active, Integer rating) {
this.id = id;
this.categoryId = categoryId;
this.properties = properties;
this.status = status;
this.createdBy = createdBy;
this.createdDateTime = createdDateTime;
this.active = active;
this.rating = rating;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getCategoryId() {
return categoryId;
}
public void setCategoryId(String categoryId) {
this.categoryId = categoryId;
}
public java.util.List<EventProperty> getProperties() {
return properties;
}
public void setProperties(java.util.List<EventProperty> properties) {
this.properties = properties;
}
public EventStatus getStatus() {
return status;
}
public void setStatus(EventStatus status) {
this.status = status;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public java.time.ZonedDateTime getCreatedDateTime() {
return createdDateTime;
}
public void setCreatedDateTime(java.time.ZonedDateTime createdDateTime) {
this.createdDateTime = createdDateTime;
}
public Boolean getActive() {
return active;
}
public void setActive(Boolean active) {
this.active = active;
}
public Integer getRating() {
return rating;
}
public void setRating(Integer rating) {
this.rating = rating;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Event that = (Event) obj;
return Objects.equals(id, that.id)
&& Objects.equals(categoryId, that.categoryId)
&& Objects.equals(properties, that.properties)
&& Objects.equals(status, that.status)
&& Objects.equals(createdBy, that.createdBy)
&& Objects.equals(createdDateTime, that.createdDateTime)
&& Objects.equals(active, that.active)
&& Objects.equals(rating, that.rating);
}
@Override
public int hashCode() {
return Objects.hash(id, categoryId, properties, status, createdBy, createdDateTime, active, rating);
}
@Override
public String toString() {
StringJoiner joiner = new StringJoiner(", ", "{ ", " }");
if (id != null) {
joiner.add("id: " + GraphQLRequestSerializer.getEntry(id));
}
if (categoryId != null) {
joiner.add("categoryId: " + GraphQLRequestSerializer.getEntry(categoryId));
}
if (properties != null) {
joiner.add("properties: " + GraphQLRequestSerializer.getEntry(properties));
}
if (status != null) {
joiner.add("status: " + GraphQLRequestSerializer.getEntry(status));
}
if (createdBy != null) {
joiner.add("createdBy: " + GraphQLRequestSerializer.getEntry(createdBy));
}
if (createdDateTime != null) {
joiner.add("createdDateTime: " + GraphQLRequestSerializer.getEntry(createdDateTime, true));
}
if (active != null) {
joiner.add("active: " + GraphQLRequestSerializer.getEntry(active));
}
if (rating != null) {
joiner.add("rating: " + GraphQLRequestSerializer.getEntry(rating));
}
return joiner.toString();
}
public static Event.Builder builder() {
return new Event.Builder();
}
public static class Builder {
private String id;
private String categoryId;
private java.util.List<EventProperty> properties;
private EventStatus status;
private String createdBy;
private java.time.ZonedDateTime createdDateTime;
private Boolean active;
private Integer rating;
public Builder() {
}
public Builder setId(String id) {
this.id = id;
return this;
}
public Builder setCategoryId(String categoryId) {
this.categoryId = categoryId;
return this;
}
public Builder setProperties(java.util.List<EventProperty> properties) {
this.properties = properties;
return this;
}
public Builder setStatus(EventStatus status) {
this.status = status;
return this;
}
public Builder setCreatedBy(String createdBy) {
this.createdBy = createdBy;
return this;
}
public Builder setCreatedDateTime(java.time.ZonedDateTime createdDateTime) {
this.createdDateTime = createdDateTime;
return this;
}
public Builder setActive(Boolean active) {
this.active = active;
return this;
}
public Builder setRating(Integer rating) {
this.rating = rating;
return this;
}
public Event build() {
return new Event(id, categoryId, properties, status, createdBy, createdDateTime, active, rating);
}
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册