未验证 提交 0b212e6d 编写于 作者: M Marvin Froeder 提交者: GitHub

Support overriding FreeMarker templates #860 (#1048)


---------
Co-authored-by: NMarvin Froeder <marvin.froeder@police.govt.nz>
Co-authored-by: NBogdan Kobylynskyi <92bogdan@gmail.com>
Co-authored-by: NMarvin Froeder <velobr@gmail.com>
上级 134bc891
......@@ -6,7 +6,10 @@ build
modules.xml
.idea/misc.xml
*.ipr
bin/
.classpath
.project
.settings/
### Maven ###
target/
......
......@@ -36,8 +36,7 @@ Please follow the steps below in order to make the changes:
./gradlew -p plugins/gradle/graphql-java-codegen-gradle-plugin clean build
# Build Maven plugin
cd plugins/maven/graphql-java-codegen-maven-plugin
mvn clean verify
mvn clean verify -f plugins/maven/graphql-java-codegen-maven-plugin/pom.xml
```
9. Make changes to the plugin code
......@@ -48,8 +47,7 @@ Please follow the steps below in order to make the changes:
./gradlew -p plugins/gradle/graphql-java-codegen-gradle-plugin clean build publishToMavenLocal
# Install Maven plugin
cd plugins/maven/graphql-java-codegen-maven-plugin
mvn clean install
mvn clean install -f plugins/maven/graphql-java-codegen-maven-plugin/pom.xml
```
11. Make sure that `example` projects are compiling and running.
此差异已折叠。
package io.github.kobylynskyi.graphql.codegen.gradle;
import com.kobylynskyi.graphql.codegen.GraphQLCodegen;
import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType;
import com.kobylynskyi.graphql.codegen.java.JavaGraphQLCodegen;
import com.kobylynskyi.graphql.codegen.kotlin.KotlinGraphQLCodegen;
import com.kobylynskyi.graphql.codegen.model.ApiInterfaceStrategy;
......@@ -54,6 +55,7 @@ public class GraphQLCodegenGradleTask extends DefaultTask implements GraphQLCode
private Map<String, String> customTypesMapping = new HashMap<>();
private Map<String, List<String>> customAnnotationsMapping = new HashMap<>();
private Map<FreeMarkerTemplateType, String> customTemplates = new HashMap<>();
private Map<String, List<String>> directiveAnnotationsMapping = new HashMap<>();
private String packageName;
private String apiPackageName;
......@@ -133,8 +135,8 @@ public class GraphQLCodegenGradleTask extends DefaultTask implements GraphQLCode
mappingConfig.setPackageName(packageName);
mappingConfig.setCustomTypesMapping(
customTypesMapping != null ? customTypesMapping : new HashMap<>());
mappingConfig.setCustomAnnotationsMapping(
customAnnotationsMapping != null ? customAnnotationsMapping : new HashMap<>());
mappingConfig.setCustomTemplates(
customTemplates != null ? customTemplates : new HashMap<>());
mappingConfig.setDirectiveAnnotationsMapping(
directiveAnnotationsMapping != null ? directiveAnnotationsMapping : new HashMap<>());
mappingConfig.setApiNameSuffix(apiNameSuffix);
......@@ -333,6 +335,17 @@ public class GraphQLCodegenGradleTask extends DefaultTask implements GraphQLCode
this.customTypesMapping = customTypesMapping;
}
@Input
@Optional
@Override
public Map<FreeMarkerTemplateType, String> getCustomTemplates() {
return customTemplates;
}
public void setCustomTemplates(Map<FreeMarkerTemplateType, String> customTemplates) {
this.customTemplates = customTemplates;
}
@Input
@Optional
@Override
......
package io.github.kobylynskyi.graphql.codegen;
import com.kobylynskyi.graphql.codegen.GraphQLCodegen;
import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType;
import com.kobylynskyi.graphql.codegen.java.JavaGraphQLCodegen;
import com.kobylynskyi.graphql.codegen.kotlin.KotlinGraphQLCodegen;
import com.kobylynskyi.graphql.codegen.model.ApiInterfaceStrategy;
......@@ -65,6 +66,9 @@ public class GraphQLCodegenMojo extends AbstractMojo implements GraphQLCodegenCo
@Parameter
private Map<String, Properties> customAnnotationsMapping;
@Parameter
private Map<FreeMarkerTemplateType, String> customTemplates;
@Parameter
private Map<String, Properties> directiveAnnotationsMapping;
......@@ -246,6 +250,7 @@ public class GraphQLCodegenMojo extends AbstractMojo implements GraphQLCodegenCo
MappingConfig mappingConfig = new MappingConfig();
mappingConfig.setPackageName(packageName);
mappingConfig.setCustomTypesMapping(convertToMap(customTypesMapping));
mappingConfig.setCustomTemplates(customTemplates);
mappingConfig.setCustomAnnotationsMapping(convertToListsMap(customAnnotationsMapping));
mappingConfig.setDirectiveAnnotationsMapping(convertToListsMap(directiveAnnotationsMapping));
mappingConfig.setApiNameSuffix(apiNameSuffix);
......@@ -737,4 +742,12 @@ public class GraphQLCodegenMojo extends AbstractMojo implements GraphQLCodegenCo
return result;
}
@Override
public Map<FreeMarkerTemplateType, String> getCustomTemplates() {
if (customTemplates == null) {
return new HashMap<>();
}
return customTemplates;
}
}
......@@ -3,6 +3,7 @@ package io.github.dreamylost.graphql.codegen
import java.util
import com.kobylynskyi.graphql.codegen.model._
import com.kobylynskyi.graphql.codegen.generators._
import sbt._
/** @author
......@@ -43,6 +44,8 @@ trait GraphQLCodegenKeys {
val customAnnotationsMapping = settingKey[util.Map[String, util.List[String]]]("customAnnotationsMapping")
val customTemplates = settingKey[util.Map[FreeMarkerTemplateType, String]]("customTemplates")
val generateEqualsAndHashCode =
settingKey[Boolean]("Specifies whether generated model classes should have equals and hashCode methods defined.")
......
......@@ -7,6 +7,7 @@ import com.kobylynskyi.graphql.codegen.model.exception.LanguageNotSupportedExcep
import com.kobylynskyi.graphql.codegen.model.GeneratedLanguage._
import com.kobylynskyi.graphql.codegen.scala.ScalaGraphQLCodegen
import com.kobylynskyi.graphql.codegen.supplier._
import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType
import sbt.{ AutoPlugin, PluginTrigger, _ }
import sbt.Keys.{ sLog, sourceManaged, _ }
import sbt.internal.util.complete.DefaultParsers.spaceDelimited
......@@ -67,6 +68,7 @@ class GraphQLCodegenPlugin(configuration: Configuration, private[codegen] val co
generateJacksonTypeIdResolver := MappingConfigConstants.DEFAULT_GENERATE_JACKSON_TYPE_ID_RESOLVER,
customTypesMapping := new JHashMap[String, String](), // TODO use scala Map, convert to java Map
customAnnotationsMapping := new JHashMap[String, JList[String]](),
customTemplates := new JHashMap[FreeMarkerTemplateType, String](),
directiveAnnotationsMapping := new JHashMap[String, JList[String]](),
javaxValidationApiVersion := None,
graphqlJavaCodegenVersion := None,
......@@ -118,17 +120,17 @@ class GraphQLCodegenPlugin(configuration: Configuration, private[codegen] val co
generateBuilder := MappingConfigConstants.DEFAULT_BUILDER,
generateApis := MappingConfigConstants.DEFAULT_GENERATE_APIS,
generateEqualsAndHashCode := MappingConfigConstants.DEFAULT_EQUALS_AND_HASHCODE,
generateImmutableModels := MappingConfigConstants.DEFAULT_GENERATE_IMMUTABLE_MODELS, // TODO change default value
generateToString := MappingConfigConstants.DEFAULT_TO_STRING,
generateImmutableModels := MappingConfigConstants.DEFAULT_GENERATE_IMMUTABLE_MODELS, // TODO change default value
generateToString := MappingConfigConstants.DEFAULT_TO_STRING,
// parent interfaces configs:
parentInterfaces := parentInterfacesConfig,
generateAllMethodInProjection := MappingConfigConstants.DEFAULT_GENERATE_ALL_METHOD,
responseProjectionMaxDepth := MappingConfigConstants.DEFAULT_RESPONSE_PROJECTION_MAX_DEPTH,
supportUnknownFields := MappingConfigConstants.DEFAULT_SUPPORT_UNKNOWN_FIELDS,
unknownFieldsPropertyName := MappingConfigConstants.DEFAULT_UNKNOWN_FIELDS_PROPERTY_NAME,
generateNoArgsConstructorOnly := MappingConfigConstants.DEFAULT_GENERATE_NOARGS_CONSTRUCTOR_ONLY,
generateModelsWithPublicFields := MappingConfigConstants.DEFAULT_GENERATE_MODELS_WITH_PUBLIC_FIELDS,
skip := false
parentInterfaces := parentInterfacesConfig,
generateAllMethodInProjection := MappingConfigConstants.DEFAULT_GENERATE_ALL_METHOD,
responseProjectionMaxDepth := MappingConfigConstants.DEFAULT_RESPONSE_PROJECTION_MAX_DEPTH,
supportUnknownFields := MappingConfigConstants.DEFAULT_SUPPORT_UNKNOWN_FIELDS,
unknownFieldsPropertyName := MappingConfigConstants.DEFAULT_UNKNOWN_FIELDS_PROPERTY_NAME,
generateNoArgsConstructorOnly := MappingConfigConstants.DEFAULT_GENERATE_NOARGS_CONSTRUCTOR_ONLY,
generateModelsWithPublicFields := MappingConfigConstants.DEFAULT_GENERATE_MODELS_WITH_PUBLIC_FIELDS,
skip := false
)
private def getMappingConfig(): Def.Initialize[MappingConfig] = Def.setting {
......@@ -149,6 +151,7 @@ class GraphQLCodegenPlugin(configuration: Configuration, private[codegen] val co
mappingConfig.setTypeResolverPrefix((GraphQLCodegenConfig / typeResolverPrefix).value.orNull)
mappingConfig.setModelValidationAnnotation((GraphQLCodegenConfig / modelValidationAnnotation).value)
mappingConfig.setCustomAnnotationsMapping((GraphQLCodegenConfig / customAnnotationsMapping).value)
mappingConfig.setCustomTemplates((GraphQLCodegenConfig / customTemplates).value)
mappingConfig.setGenerateEqualsAndHashCode((GraphQLCodegenConfig / generateEqualsAndHashCode).value)
mappingConfig.setGenerateImmutableModels((GraphQLCodegenConfig / generateImmutableModels).value)
mappingConfig.setGenerateToString((GraphQLCodegenConfig / generateToString).value)
......
......@@ -48,7 +48,7 @@ public class FreeMarkerTemplateFilesCreator {
}
try (FileWriter fileWriter = new FileWriter(javaSourceFile)) {
Template template = FreeMarkerTemplatesRegistry.getTemplateWithLang(language, templateType);
Template template = getTemplateForTypeAndLanguage(mappingContext, templateType, language);
template.process(dataModel, fileWriter);
} catch (Exception e) {
throw new UnableToCreateFileException(e);
......@@ -56,6 +56,20 @@ public class FreeMarkerTemplateFilesCreator {
return javaSourceFile;
}
private static Template getTemplateForTypeAndLanguage(MappingContext mappingContext,
FreeMarkerTemplateType templateType,
GeneratedLanguage language) {
String templatePath = null;
if (mappingContext.getCustomTemplates() != null) {
templatePath = mappingContext.getCustomTemplates().get(templateType);
}
if (templatePath != null) {
return FreeMarkerTemplatesRegistry.getCustomTemplates(templatePath);
} else {
return FreeMarkerTemplatesRegistry.getTemplateWithLang(language, templateType);
}
}
private static File getFileTargetDirectory(Map<String, Object> dataModel, File outputDir) {
File targetDir;
Object packageName = dataModel.get(DataModelFields.PACKAGE);
......
......@@ -22,9 +22,9 @@ class FreeMarkerTemplatesRegistry {
private static final EnumMap<GeneratedLanguage, EnumMap<FreeMarkerTemplateType, Template>> templateMap =
new EnumMap<>(GeneratedLanguage.class);
private static final Configuration configuration = buildFreeMarkerTemplateConfiguration();
static {
Configuration configuration = buildFreeMarkerTemplateConfiguration();
try {
templateMap.put(GeneratedLanguage.JAVA, getTemplates(configuration, GeneratedLanguage.JAVA));
templateMap.put(GeneratedLanguage.SCALA, getTemplates(configuration, GeneratedLanguage.SCALA));
......@@ -70,4 +70,12 @@ class FreeMarkerTemplatesRegistry {
return configuration;
}
public static Template getCustomTemplates(String templatePath) {
try {
return configuration.getTemplate(templatePath);
} catch (IOException e) {
throw new UnableToLoadFreeMarkerTemplateException(e);
}
}
}
package com.kobylynskyi.graphql.codegen.model;
import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType;
import java.util.List;
import java.util.Map;
import java.util.Set;
......@@ -27,6 +28,13 @@ public interface GraphQLCodegenConfiguration {
* @return mappings from GraphqlType to JavaType
*/
Map<String, String> getCustomTypesMapping();
/**
* Can be used to supply paths to custom FreeMarker templates for code generation.
*
* @return a map, where key is a tempalte type and a value is path to a FreeMarker template
*/
Map<FreeMarkerTemplateType, String> getCustomTemplates();
/**
* Can be used to supply custom annotations (serializers) for scalars.
......
package com.kobylynskyi.graphql.codegen.model;
import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
......@@ -85,6 +86,7 @@ public class MappingConfig implements GraphQLCodegenConfiguration, Combinable<Ma
private Set<String> parametrizedResolverAnnotations = new HashSet<>();
private Map<String, String> customTypesMapping = new HashMap<>();
private Map<FreeMarkerTemplateType, String> customTemplates = new HashMap<>();
private Set<String> typesAsInterfaces = new HashSet<>();
......@@ -94,9 +96,9 @@ public class MappingConfig implements GraphQLCodegenConfiguration, Combinable<Ma
private GeneratedLanguage generatedLanguage;
private static <T> Map<String, T> combineMap(Map<String, T> thisMap, Map<String, T> otherMap) {
private static <K, T> Map<K, T> combineMap(Map<K, T> thisMap, Map<K, T> otherMap) {
if (thisMap != null && otherMap != null) {
Map<String, T> resultMap = new HashMap<>();
Map<K, T> resultMap = new HashMap<>();
resultMap.putAll(thisMap);
resultMap.putAll(otherMap);
return resultMap;
......@@ -186,6 +188,7 @@ public class MappingConfig implements GraphQLCodegenConfiguration, Combinable<Ma
fieldsWithResolvers = combineSet(fieldsWithResolvers, source.fieldsWithResolvers);
fieldsWithoutResolvers = combineSet(fieldsWithoutResolvers, source.fieldsWithoutResolvers);
customTypesMapping = combineMap(customTypesMapping, source.customTypesMapping);
customTemplates = combineMap(customTemplates, source.customTemplates);
customAnnotationsMapping = combineMap(customAnnotationsMapping, source.customAnnotationsMapping);
directiveAnnotationsMapping = combineMap(directiveAnnotationsMapping, source.directiveAnnotationsMapping);
resolverArgumentAnnotations = combineSet(resolverArgumentAnnotations, source.resolverArgumentAnnotations);
......@@ -244,6 +247,28 @@ public class MappingConfig implements GraphQLCodegenConfiguration, Combinable<Ma
this.customTypesMapping = customTypesMapping;
}
/**
* Provide a path to a custom template for the specific FreeMarker template type (if absent).
*
* @param from the from
* @param to the to
*/
public void putCustomTemplatesIfAbsent(FreeMarkerTemplateType from, String to) {
if (customTemplates == null) {
customTemplates = new HashMap<>();
}
customTemplates.computeIfAbsent(from, k -> to);
}
@Override
public Map<FreeMarkerTemplateType, String> getCustomTemplates() {
return customTemplates;
}
public void setCustomTemplates(Map<FreeMarkerTemplateType, String> customTemplates) {
this.customTemplates = customTemplates;
}
@Override
public Map<String, List<String>> getCustomAnnotationsMapping() {
return customAnnotationsMapping;
......
package com.kobylynskyi.graphql.codegen.model;
import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType;
import com.kobylynskyi.graphql.codegen.mapper.DataModelMapper;
import com.kobylynskyi.graphql.codegen.mapper.DataModelMapperFactory;
import com.kobylynskyi.graphql.codegen.mapper.FieldDefinitionToParameterMapper;
......@@ -83,6 +84,11 @@ public class MappingContext implements GraphQLCodegenConfiguration {
public Map<String, String> getCustomTypesMapping() {
return config.getCustomTypesMapping();
}
@Override
public Map<FreeMarkerTemplateType, String> getCustomTemplates() {
return config.getCustomTemplates();
}
@Override
public Map<String, List<String>> getCustomAnnotationsMapping() {
......
package com.kobylynskyi.graphql.codegen;
import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType;
import com.kobylynskyi.graphql.codegen.java.JavaGraphQLCodegen;
import com.kobylynskyi.graphql.codegen.model.MappingConfig;
import com.kobylynskyi.graphql.codegen.utils.Utils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
import static com.kobylynskyi.graphql.codegen.TestUtils.assertFileContainsElements;
import static java.util.Collections.singletonList;
class GraphQLCodegenCustomTemplatesTest {
private final File outputBuildDir = new File("build/generated");
private final File outputJavaClassesDir = new File("build/generated/com/kobylynskyi/graphql/test1");
private MappingConfig mappingConfig;
@BeforeEach
void init() {
mappingConfig = new MappingConfig();
mappingConfig.setPackageName("com.kobylynskyi.graphql.test1");
mappingConfig.setGenerateClient(true);
}
@AfterEach
void cleanup() {
Utils.deleteDir(outputBuildDir);
}
@Test
void generate_CustomTemplates_Type() throws Exception {
mappingConfig.putCustomTemplatesIfAbsent(FreeMarkerTemplateType.TYPE, "/template/record_type.ftl");
generate("src/test/resources/schemas/test.graphqls");
File[] files = Objects.requireNonNull(outputJavaClassesDir.listFiles());
assertFileContainsElements(files, "Event.java",
"public record Event (");
}
private void generate(String path) throws IOException {
new JavaGraphQLCodegen(singletonList(path),
outputBuildDir, mappingConfig, TestUtils.getStaticGeneratedInfo(mappingConfig))
.generate();
}
}
<#assign MapperUtil=statics["com.kobylynskyi.graphql.codegen.java.JavaGraphQLTypeMapper"]>
<#if package?has_content>
package ${package};
</#if>
public record ${className} (
<#if fields?has_content>
<#list fields as field>
${field.type} ${field.name}<#if field.defaultValue?has_content> = ${field.defaultValue}</#if><#if field?has_next>,</#if>
</#list>
</#if>
)
{
public ${className}() {
<#list fields as field><#if field.defaultValue?has_content> ${field.name} = ${field.defaultValue};</#if></#list>
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册