Skip to content

Commit 209ecf8

Browse files
Modifications to customize the code templates to use in code generation. To achieve this feature the follwing changes has been added:
* Modified GraphqlMavenPlugin POJO with new param tempaltes to configure with templates to overwrite. The key is the alias of the template and the value is the classpath resource related to the file. PluginCofiguration and PluginConfigurationTestHelper has been modified to add support to new new property * CodeGenerator has been modifiied to check if a template has been overwritten. For every invocation to generateTargetFiles, the template to use is resolved with an invocation to the new method resolveTemplate. The maker easier the management of the available templates, all static properties with the classpath resources for the templates has been refactored to a new enumeration: CodeTemplate. Also, each enumeration entry is classfied with it scope: client, server or common (applies to both). The available scopes have also been definied in a new enumeration: CodeTemplateScope Unit and Integration tests changes: * Added unit test for CodeGenerator.resolveTemplate * Added integration tests to validate both custom generation of server an client code: CustomTemplatesClientTest and CustomTemplatesServerTest. Both tests extends AbstractCustomTemplateIntegrationTest wich defines assertions for customized code generation. Spring configuration has been added for each test: CustomTemplates_Client_SpringConfiguration and CustomTemplates_Server_SpringConfiguration. Both configuration extend AbstractCustomTemplatesSpringConfiguration which configures code generation with customized templates * Customized code templates has been added to graphql-maven-plugin-logic/src/test/resources/tempaltes_personalization. Every templates generates the first line of the file as "/** This template is custom **/"
1 parent d077e30 commit 209ecf8

31 files changed

+1615
-41
lines changed

graphql-maven-plugin-logic/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@
8989
<artifactId>spring-boot-starter-data-jpa</artifactId>
9090
<scope>provided</scope>
9191
</dependency>
92+
<dependency>
93+
<groupId>org.javatuples</groupId>
94+
<artifactId>javatuples</artifactId>
95+
<scope>test</scope>
96+
</dependency>
9297

9398
<!-- Other dependencies -->
9499
<dependency>

graphql-maven-plugin-logic/src/main/java/com/graphql_java_generator/plugin/CodeGenerator.java

Lines changed: 31 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -47,26 +47,6 @@
4747
@Component
4848
public class CodeGenerator {
4949

50-
// Templates for both client and server generation
51-
private static final String PATH_VELOCITY_TEMPLATE_OBJECT = "templates/object_type.vm.java";
52-
private static final String PATH_VELOCITY_TEMPLATE_INTERFACE = "templates/interface_type.vm.java";
53-
private static final String PATH_VELOCITY_TEMPLATE_ENUM = "templates/enum_type.vm.java";
54-
private static final String PATH_VELOCITY_TEMPLATE_UNION = "templates/union_type.vm.java";
55-
// Templates for client generation only
56-
private static final String PATH_VELOCITY_TEMPLATE_CUSTOM_SCALAR_REGISTRY_INITIALIZER = "templates/client_CustomScalarRegistryInitializer.vm.java";
57-
private static final String PATH_VELOCITY_TEMPLATE_QUERY_MUTATION_SUBSCRIPTION = "templates/client_query_mutation_subscription_type.vm.java";
58-
private static final String PATH_VELOCITY_TEMPLATE_QUERY_TARGET_TYPE = "templates/client_query_target_type.vm.java";
59-
private static final String PATH_VELOCITY_TEMPLATE_JACKSON_DESERIALIZER = "templates/client_jackson_deserialize.vm.java";
60-
// Templates for server generation only
61-
private static final String PATH_VELOCITY_TEMPLATE_BATCHLOADERDELEGATE = "templates/server_BatchLoaderDelegate.vm.java";
62-
private static final String PATH_VELOCITY_TEMPLATE_BATCHLOADERDELEGATEIMPL = "templates/server_BatchLoaderDelegateImpl.vm.java";
63-
// private static final String PATH_VELOCITY_TEMPLATE_CUSTOM_SCALARS = "templates/server_GraphQLScalarType.vm.java";
64-
private static final String PATH_VELOCITY_TEMPLATE_DATAFETCHER = "templates/server_GraphQLDataFetchers.vm.java";
65-
private static final String PATH_VELOCITY_TEMPLATE_DATAFETCHERDELEGATE = "templates/server_GraphQLDataFetchersDelegate.vm.java";
66-
private static final String PATH_VELOCITY_TEMPLATE_GRAPHQLUTIL = "templates/server_GraphQLUtil.vm.java";
67-
private static final String PATH_VELOCITY_TEMPLATE_PROVIDER = "templates/server_GraphQLProvider.vm.java";
68-
private static final String PATH_VELOCITY_TEMPLATE_SERVER = "templates/server_GraphQLServerMain.vm.java";
69-
7050
public static final String FILE_TYPE_JACKSON_DESERIALIZER = "Jackson deserializer";
7151

7252
@Autowired
@@ -103,33 +83,33 @@ public CodeGenerator() {
10383
public int generateCode() throws IOException {
10484

10585
int i = 0;
106-
i += generateTargetFiles(documentParser.getObjectTypes(), "object", PATH_VELOCITY_TEMPLATE_OBJECT);
107-
i += generateTargetFiles(documentParser.getInterfaceTypes(), "interface", PATH_VELOCITY_TEMPLATE_INTERFACE);
108-
i += generateTargetFiles(documentParser.getUnionTypes(), "union", PATH_VELOCITY_TEMPLATE_UNION);
109-
i += generateTargetFiles(documentParser.getEnumTypes(), "enum", PATH_VELOCITY_TEMPLATE_ENUM);
86+
i += generateTargetFiles(documentParser.getObjectTypes(), "object", resolveTemplate(CodeTemplate.OBJECT));
87+
i += generateTargetFiles(documentParser.getInterfaceTypes(), "interface", resolveTemplate(CodeTemplate.INTERFACE));
88+
i += generateTargetFiles(documentParser.getUnionTypes(), "union", resolveTemplate(CodeTemplate.UNION));
89+
i += generateTargetFiles(documentParser.getEnumTypes(), "enum", resolveTemplate(CodeTemplate.ENUM));
11090

11191
switch (pluginConfiguration.getMode()) {
11292
case server:
11393
i += generateServerFiles();
11494
break;
11595
case client:
11696
i += generateTargetFiles(documentParser.getQueryTypes(), "query",
117-
PATH_VELOCITY_TEMPLATE_QUERY_MUTATION_SUBSCRIPTION);
97+
resolveTemplate(CodeTemplate.QUERY_MUTATION_SUBSCRIPTION));
11898
i += generateTargetFiles(documentParser.getMutationTypes(), "mutation",
119-
PATH_VELOCITY_TEMPLATE_QUERY_MUTATION_SUBSCRIPTION);
99+
resolveTemplate(CodeTemplate.QUERY_MUTATION_SUBSCRIPTION));
120100
i += generateTargetFiles(documentParser.getSubscriptionTypes(), "subscription",
121-
PATH_VELOCITY_TEMPLATE_QUERY_MUTATION_SUBSCRIPTION);
101+
resolveTemplate(CodeTemplate.QUERY_MUTATION_SUBSCRIPTION));
122102

123103
// Files for Custom Scalars
124104
VelocityContext context = new VelocityContext();
125105
context.put("pluginConfiguration", pluginConfiguration);
126106
context.put("customScalars", documentParser.customScalars);
127107
i += generateOneFile(getJavaFile("CustomScalarRegistryInitializer"),
128108
"Generating CustomScalarRegistryInitializer", context,
129-
PATH_VELOCITY_TEMPLATE_CUSTOM_SCALAR_REGISTRY_INITIALIZER);
109+
resolveTemplate(CodeTemplate.CUSTOM_SCALAR_REGISTRY_INITIALIZER));
130110
//
131111
i += generateTargetFiles(documentParser.customScalars, FILE_TYPE_JACKSON_DESERIALIZER,
132-
PATH_VELOCITY_TEMPLATE_JACKSON_DESERIALIZER);
112+
resolveTemplate(CodeTemplate.JACKSON_DESERIALIZER));
133113
i += generateQueryTargetType();
134114
break;
135115
}
@@ -223,7 +203,7 @@ int generateQueryTargetType() {
223203
context.put("objectName", objectName);
224204
context.put("query", query);
225205

226-
i += generateOneFile(targetFile, msg, context, PATH_VELOCITY_TEMPLATE_QUERY_TARGET_TYPE);
206+
i += generateOneFile(targetFile, msg, context, resolveTemplate(CodeTemplate.QUERY_TARGET_TYPE));
227207
}
228208
}
229209
return i;
@@ -253,28 +233,28 @@ int generateServerFiles() throws IOException {
253233

254234
int ret = 0;
255235
ret += generateOneFile(getJavaFile("GraphQLServerMain"), "generating GraphQLServerMain", context,
256-
PATH_VELOCITY_TEMPLATE_SERVER);
236+
resolveTemplate(CodeTemplate.SERVER));
257237
ret += generateOneFile(getJavaFile("GraphQLProvider"), "generating GraphQLProvider", context,
258-
PATH_VELOCITY_TEMPLATE_PROVIDER);
238+
resolveTemplate(CodeTemplate.PROVIDER));
259239
ret += generateOneFile(getJavaFile("GraphQLDataFetchers"), "generating GraphQLDataFetchers", context,
260-
PATH_VELOCITY_TEMPLATE_DATAFETCHER);
240+
resolveTemplate(CodeTemplate.DATAFETCHER));
261241
ret += generateOneFile(getJavaFile("GraphQLUtil"), "generating GraphQLUtil", context,
262-
PATH_VELOCITY_TEMPLATE_GRAPHQLUTIL);
242+
resolveTemplate(CodeTemplate.GRAPHQLUTIL));
263243

264244
for (DataFetchersDelegate dataFetcherDelegate : documentParser.dataFetchersDelegates) {
265245
context.put("dataFetcherDelegate", dataFetcherDelegate);
266246
ret += generateOneFile(getJavaFile(dataFetcherDelegate.getPascalCaseName()),
267247
"generating " + dataFetcherDelegate.getPascalCaseName(), context,
268-
PATH_VELOCITY_TEMPLATE_DATAFETCHERDELEGATE);
248+
resolveTemplate(CodeTemplate.DATAFETCHERDELEGATE));
269249
}
270250

271251
ret += generateOneFile(getJavaFile("BatchLoaderDelegate"), "generating BatchLoaderDelegate", context,
272-
PATH_VELOCITY_TEMPLATE_BATCHLOADERDELEGATE);
252+
resolveTemplate(CodeTemplate.BATCHLOADERDELEGATE));
273253
for (BatchLoader batchLoader : documentParser.batchLoaders) {
274254
String name = "BatchLoaderDelegate" + batchLoader.getType().getClassSimpleName() + "Impl";
275255
context.put("batchLoader", batchLoader);
276256
ret += generateOneFile(getJavaFile(name), "generating " + name, context,
277-
PATH_VELOCITY_TEMPLATE_BATCHLOADERDELEGATEIMPL);
257+
resolveTemplate(CodeTemplate.BATCHLOADERDELEGATEIMPL));
278258
}
279259

280260
return ret;
@@ -393,4 +373,18 @@ List<String> getImportList() {
393373
}
394374
return ret;
395375
}
376+
377+
/**
378+
* Resolves the template for the given key
379+
* @param templateKey
380+
* @param defaultValue
381+
* @return
382+
*/
383+
protected String resolveTemplate(CodeTemplate template) {
384+
if(pluginConfiguration.getTemplates().containsKey(template.name())) {
385+
return pluginConfiguration.getTemplates().get(template.name());
386+
} else {
387+
return template.getDefaultValue();
388+
}
389+
}
396390
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.graphql_java_generator.plugin;
2+
3+
import lombok.AccessLevel;
4+
import lombok.Getter;
5+
import lombok.NonNull;
6+
import lombok.RequiredArgsConstructor;
7+
8+
/**
9+
* Enumeration that defines the available templates for code generation
10+
* @author ggomez
11+
*
12+
*/
13+
@Getter
14+
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
15+
public enum CodeTemplate {
16+
17+
18+
OBJECT(CodeTemplateScope.COMMON, "templates/object_type.vm.java"),
19+
INTERFACE(CodeTemplateScope.COMMON, "templates/interface_type.vm.java"),
20+
ENUM(CodeTemplateScope.COMMON, "templates/enum_type.vm.java"),
21+
UNION(CodeTemplateScope.COMMON, "templates/union_type.vm.java"),
22+
CUSTOM_SCALAR_REGISTRY_INITIALIZER(CodeTemplateScope.CLIENT, "templates/client_CustomScalarRegistryInitializer.vm.java"),
23+
QUERY_MUTATION_SUBSCRIPTION(CodeTemplateScope.CLIENT, "templates/client_query_mutation_subscription_type.vm.java"),
24+
QUERY_TARGET_TYPE(CodeTemplateScope.CLIENT, "templates/client_query_target_type.vm.java"),
25+
JACKSON_DESERIALIZER(CodeTemplateScope.CLIENT, "templates/client_jackson_deserialize.vm.java"),
26+
BATCHLOADERDELEGATE(CodeTemplateScope.SERVER, "templates/server_BatchLoaderDelegate.vm.java"),
27+
BATCHLOADERDELEGATEIMPL(CodeTemplateScope.SERVER, "templates/server_BatchLoaderDelegateImpl.vm.java"),
28+
DATAFETCHER(CodeTemplateScope.SERVER, "templates/server_GraphQLDataFetchers.vm.java"),
29+
DATAFETCHERDELEGATE(CodeTemplateScope.SERVER, "templates/server_GraphQLDataFetchersDelegate.vm.java"),
30+
GRAPHQLUTIL(CodeTemplateScope.SERVER, "templates/server_GraphQLUtil.vm.java"),
31+
PROVIDER(CodeTemplateScope.SERVER, "templates/server_GraphQLProvider.vm.java"),
32+
SERVER(CodeTemplateScope.SERVER, "templates/server_GraphQLServerMain.vm.java");
33+
34+
/**
35+
* The de
36+
*/
37+
@NonNull
38+
private CodeTemplateScope scope;
39+
40+
/**
41+
* The default value to use
42+
*/
43+
@NonNull
44+
private String defaultValue;
45+
46+
47+
48+
49+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.graphql_java_generator.plugin;
2+
3+
/**
4+
* Enumeration that defines the {@link CodeTemplate} scopes avaible
5+
* @author ggomez
6+
*
7+
*/
8+
public enum CodeTemplateScope {
9+
10+
/**
11+
* Scope for just client code
12+
*/
13+
CLIENT,
14+
15+
/**
16+
* Scope for server code
17+
*/
18+
SERVER,
19+
20+
/**
21+
* Scope for both client and server code
22+
*/
23+
COMMON;
24+
25+
}

graphql-maven-plugin-logic/src/main/java/com/graphql_java_generator/plugin/PluginConfiguration.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,12 @@ public interface PluginConfiguration {
108108

109109
/** The folder where the generated classes will be generated */
110110
public File getTargetSourceFolder();
111-
111+
112+
/**
113+
* The overwritting templates
114+
*/
115+
public Map<String, String> getTemplates();
116+
112117
/**
113118
* Returns true if shall copy graphql sources (graphql-java-runtime) source code
114119
* @return
@@ -129,6 +134,8 @@ default public void logConfiguration() {
129134
getLog().debug(" TargetClassFolder: " + getTargetClassFolder());
130135
getLog().debug(" TargetSourceFolder: " + getTargetSourceFolder());
131136
getLog().debug(" CopyGraphQLJavaSources: " + isCopyGraphQLJavaSources());
137+
getLog().debug(" Templates: " + (Objects.nonNull(getTemplates())?
138+
getTemplates().entrySet().stream().map(entry -> String.format("%s=%s", entry.getKey(), entry.getValue())).collect(Collectors.joining(", ")): StringUtils.EMPTY) );
132139
}
133140
}
134141
}

graphql-maven-plugin-logic/src/test/java/com/graphql_java_generator/plugin/CodeGeneratorTest.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package com.graphql_java_generator.plugin;
22

33
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
45
import static org.junit.jupiter.api.Assertions.assertNotNull;
56
import static org.junit.jupiter.api.Assertions.assertTrue;
6-
import static org.junit.jupiter.api.Assertions.assertFalse;
77
import static org.mockito.ArgumentMatchers.any;
88
import static org.mockito.ArgumentMatchers.anyString;
99
import static org.mockito.Mockito.mock;
@@ -47,7 +47,6 @@
4747
@ContextConfiguration(classes = { AllGraphQLCases_Server_SpringConfiguration.class })
4848
class CodeGeneratorTest {
4949

50-
5150
@Resource
5251
ApplicationContext context;
5352
@Resource
@@ -252,6 +251,26 @@ void testGenerateCode_skipCopySources() throws IOException {
252251
assertFalse(targetRuntimeClassesSourceFolder.exists());
253252
}
254253

254+
/**
255+
* Test for validating default template resolution
256+
*/
257+
@Test
258+
@DirtiesContext
259+
protected void testResolveTemplateDefault() {
260+
pluginConfiguration.templates.clear();
261+
assertEquals(CodeTemplate.BATCHLOADERDELEGATE.getDefaultValue(), this.codeGenerator.resolveTemplate(CodeTemplate.BATCHLOADERDELEGATE));;
262+
}
263+
264+
/**
265+
* Test for validating customized template resolution
266+
*/
267+
@Test
268+
@DirtiesContext
269+
protected void testResolveTemplateCustom() {
270+
pluginConfiguration.templates.clear();
271+
pluginConfiguration.templates.put(CodeTemplate.BATCHLOADERDELEGATE.name(), "/my/custom/template");
272+
assertEquals("/my/custom/template", this.codeGenerator.resolveTemplate(CodeTemplate.BATCHLOADERDELEGATE));;
273+
}
255274

256275
/**
257276
* Creates a mock graphql-java-runtime-sources.jar
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.graphql_java_generator.plugin.compilation_tests;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import java.io.File;
6+
import java.util.Arrays;
7+
8+
import org.apache.commons.io.FileUtils;
9+
10+
/**
11+
* Base integration test for Custom templates scenario
12+
* Offers methods to assert customized code validation
13+
* @author ggomez
14+
*
15+
*/
16+
public abstract class AbstractCustomTemplateIntegrationTest extends AbstractIntegrationTest {
17+
18+
protected static final String CUSTOMIZED_CODE_FIRST_LINE = "/** This template is custom **/";
19+
20+
/**
21+
* Helper method that validates that given dir contains files generated with custom templates
22+
* Custom template file are identified by it's first line. It's value is \/** This template is custom **\/
23+
* @param generatedSourcesDir
24+
*/
25+
protected void assertCustomTemplateGeneration(File sourcesDirectory) {
26+
// Validate that every file generated has been generated with the templates in src/test/resources/templates_personalization
27+
Arrays.stream(sourcesDirectory.listFiles())
28+
.filter(generatedFile -> generatedFile.exists() && generatedFile.isFile())
29+
.forEach(generatedFile-> {
30+
try {
31+
assertEquals(CUSTOMIZED_CODE_FIRST_LINE, FileUtils.readLines(generatedFile, "UTF-8").get(0));
32+
} catch(Exception e) {
33+
throw new RuntimeException(e);
34+
}
35+
});
36+
}
37+
38+
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.graphql_java_generator.plugin.compilation_tests;
2+
3+
import java.io.File;
4+
import java.io.IOException;
5+
6+
import org.junit.jupiter.api.BeforeEach;
7+
import org.junit.jupiter.api.Test;
8+
import org.junit.jupiter.api.extension.ExtendWith;
9+
import org.springframework.test.annotation.DirtiesContext;
10+
import org.springframework.test.context.ContextConfiguration;
11+
import org.springframework.test.context.junit.jupiter.SpringExtension;
12+
13+
import graphql.mavenplugin_notscannedbyspring.CustomTemplates_Client_SpringConfiguration;
14+
15+
@ExtendWith(SpringExtension.class)
16+
@ContextConfiguration(classes = { CustomTemplates_Client_SpringConfiguration.class })
17+
class CustomTemplatesClientTest extends AbstractCustomTemplateIntegrationTest {
18+
19+
// Everything is in the AbstractIntegrationTest class.
20+
21+
// The only aim of this class, is to have its own Spring Configuration
22+
@BeforeEach
23+
public void setUp() throws IOException {
24+
graphqlTestHelper.checkSchemaStringProvider("allGraphQLCases.graphqls");
25+
}
26+
27+
/**
28+
* This test will be executed for each concrete subclass of this class
29+
*
30+
* @throws MojoExecutionException
31+
* @throws IOException
32+
*/
33+
@Test
34+
@DirtiesContext // We need to forget the previous parsing (or everything may be doubled)
35+
void testGenerateCode() throws IOException {
36+
super.testGenerateCode();
37+
38+
// Validate that every file generated has been generated with the templates in src/test/resources/templates_personalization
39+
File generatedSourcesDir = new File(this.pluginConfiguration.getTargetSourceFolder(), pluginConfiguration.getPackageName().replaceAll("\\.", File.separator));
40+
assertCustomTemplateGeneration(generatedSourcesDir);
41+
42+
43+
}
44+
45+
46+
}

0 commit comments

Comments
 (0)