Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Made possible to use custom templates #1048

Merged
merged 10 commits into from
Mar 19, 2023
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ build
modules.xml
.idea/misc.xml
*.ipr

bin/
.classpath
.project
.settings/

### Maven ###
target/
Expand Down
6 changes: 2 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
131 changes: 66 additions & 65 deletions docs/codegen-options.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.kobylynskyi.graphql.codegen.gradle;

import com.kobylynskyi.graphql.codegen.GraphQLCodegen;
import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType;
import com.kobylynskyi.graphql.codegen.java.JavaGraphQLCodegen;
import com.kobylynskyi.graphql.codegen.kotlin.KotlinGraphQLCodegen;
import com.kobylynskyi.graphql.codegen.model.ApiInterfaceStrategy;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -133,8 +135,8 @@ public void generate() throws Exception {
mappingConfig.setPackageName(packageName);
mappingConfig.setCustomTypesMapping(
customTypesMapping != null ? customTypesMapping : new HashMap<>());
mappingConfig.setCustomAnnotationsMapping(
customAnnotationsMapping != null ? customAnnotationsMapping : new HashMap<>());
mappingConfig.setCustomTemplates(
customTemplates != null ? customTemplates : new HashMap<>());
mappingConfig.setDirectiveAnnotationsMapping(
directiveAnnotationsMapping != null ? directiveAnnotationsMapping : new HashMap<>());
mappingConfig.setApiNameSuffix(apiNameSuffix);
Expand Down Expand Up @@ -333,6 +335,17 @@ public void setCustomTypesMapping(Map<String, String> customTypesMapping) {
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
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.kobylynskyi.graphql.codegen;

import com.kobylynskyi.graphql.codegen.GraphQLCodegen;
import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType;
import com.kobylynskyi.graphql.codegen.java.JavaGraphQLCodegen;
import com.kobylynskyi.graphql.codegen.kotlin.KotlinGraphQLCodegen;
import com.kobylynskyi.graphql.codegen.model.ApiInterfaceStrategy;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -246,6 +250,7 @@ public void execute() throws MojoExecutionException {
MappingConfig mappingConfig = new MappingConfig();
mappingConfig.setPackageName(packageName);
mappingConfig.setCustomTypesMapping(convertToMap(customTypesMapping));
mappingConfig.setCustomTemplates(customTemplates);
mappingConfig.setCustomAnnotationsMapping(convertToListsMap(customAnnotationsMapping));
mappingConfig.setDirectiveAnnotationsMapping(convertToListsMap(directiveAnnotationsMapping));
mappingConfig.setApiNameSuffix(apiNameSuffix);
Expand Down Expand Up @@ -737,4 +742,12 @@ private static Map<String, String> convertToMap(Properties properties) {
return result;
}

@Override
public Map<FreeMarkerTemplateType, String> getCustomTemplates() {
if (customTemplates == null) {
return new HashMap<>();
}
return customTemplates;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,28 @@ public static File create(MappingContext mappingContext,
}

try (FileWriter fileWriter = new FileWriter(javaSourceFile)) {
Template template = FreeMarkerTemplatesRegistry.getTemplateWithLang(language, templateType);
Template template = getTemplateForTypeAndLanguage(mappingContext, templateType, language);
template.process(dataModel, fileWriter);
} catch (Exception e) {
throw new UnableToCreateFileException(e);
}
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -70,4 +70,12 @@ private static Configuration buildFreeMarkerTemplateConfiguration() {
return configuration;
}

public static Template getCustomTemplates(String templatePath) {
try {
return configuration.getTemplate(templatePath);
} catch (IOException e) {
throw new UnableToLoadFreeMarkerTemplateException(e);
}
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.kobylynskyi.graphql.codegen.model;

import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.kobylynskyi.graphql.codegen.model;

import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -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<>();

Expand All @@ -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;
Expand Down Expand Up @@ -186,6 +188,7 @@ public void combine(MappingConfig source) {
fieldsWithResolvers = combineSet(fieldsWithResolvers, source.fieldsWithResolvers);
fieldsWithoutResolvers = combineSet(fieldsWithoutResolvers, source.fieldsWithoutResolvers);
customTypesMapping = combineMap(customTypesMapping, source.customTypesMapping);
customTemplates = combineMap(customTemplates, source.customTemplates);
customAnnotationsMapping = combineMap(customAnnotationsMapping, source.customAnnotationsMapping);
directiveAnnotationsMapping = combineMap(directiveAnnotationsMapping, source.directiveAnnotationsMapping);
resolverArgumentAnnotations = combineSet(resolverArgumentAnnotations, source.resolverArgumentAnnotations);
Expand Down Expand Up @@ -244,6 +247,28 @@ public void setCustomTypesMapping(Map<String, String> customTypesMapping) {
this.customTypesMapping = customTypesMapping;
}

/**
* Provide a path to a custom template for the specific FreeMarker template type (if absent).
*
* @param from the from
* @param to the to
*/
public void putCustomTemplatesIfAbsent(FreeMarkerTemplateType from, String to) {
if (customTemplates == null) {
customTemplates = new HashMap<>();
}
customTemplates.computeIfAbsent(from, k -> to);
}

@Override
public Map<FreeMarkerTemplateType, String> getCustomTemplates() {
return customTemplates;
}

public void setCustomTemplates(Map<FreeMarkerTemplateType, String> customTemplates) {
this.customTemplates = customTemplates;
}

@Override
public Map<String, List<String>> getCustomAnnotationsMapping() {
return customAnnotationsMapping;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.kobylynskyi.graphql.codegen.model;

import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType;
import com.kobylynskyi.graphql.codegen.mapper.DataModelMapper;
import com.kobylynskyi.graphql.codegen.mapper.DataModelMapperFactory;
import com.kobylynskyi.graphql.codegen.mapper.FieldDefinitionToParameterMapper;
Expand Down Expand Up @@ -83,6 +84,11 @@ public Boolean isGenerateSealedInterfaces() {
public Map<String, String> getCustomTypesMapping() {
return config.getCustomTypesMapping();
}

@Override
public Map<FreeMarkerTemplateType, String> getCustomTemplates() {
return config.getCustomTemplates();
}

@Override
public Map<String, List<String>> getCustomAnnotationsMapping() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.kobylynskyi.graphql.codegen;

import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType;
import com.kobylynskyi.graphql.codegen.java.JavaGraphQLCodegen;
import com.kobylynskyi.graphql.codegen.model.MappingConfig;
import com.kobylynskyi.graphql.codegen.utils.Utils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.IOException;
import java.util.Objects;

import static com.kobylynskyi.graphql.codegen.TestUtils.assertFileContainsElements;
import static java.util.Collections.singletonList;

class GraphQLCodegenCustomTemplatesTest {

private final File outputBuildDir = new File("build/generated");
private final File outputJavaClassesDir = new File("build/generated/com/kobylynskyi/graphql/test1");

private MappingConfig mappingConfig;

@BeforeEach
void init() {
mappingConfig = new MappingConfig();
mappingConfig.setPackageName("com.kobylynskyi.graphql.test1");
mappingConfig.setGenerateClient(true);
}

@AfterEach
void cleanup() {
Utils.deleteDir(outputBuildDir);
}

@Test
void generate_CustomTemplates_Type() throws Exception {
mappingConfig.putCustomTemplatesIfAbsent(FreeMarkerTemplateType.TYPE, "/template/record_type.ftl");

generate("src/test/resources/schemas/test.graphqls");

File[] files = Objects.requireNonNull(outputJavaClassesDir.listFiles());
assertFileContainsElements(files, "Event.java",
"public record Event (");
}

private void generate(String path) throws IOException {
new JavaGraphQLCodegen(singletonList(path),
outputBuildDir, mappingConfig, TestUtils.getStaticGeneratedInfo(mappingConfig))
.generate();
}

}
Loading