Skip to content

Commit 605345d

Browse files
committed
Add test verifying partial template works; Extract accessAwareFieldValueResolver; Add test verifying priority fo values extracted from object
1 parent 76d3964 commit 605345d

File tree

4 files changed

+108
-28
lines changed

4 files changed

+108
-28
lines changed

modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/HandlebarsEngineAdapter.java

+4-26
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import lombok.Setter;
3434
import org.openapitools.codegen.api.AbstractTemplatingEngineAdapter;
3535
import org.openapitools.codegen.api.TemplatingExecutor;
36+
import org.openapitools.codegen.templating.handlebars.AccessAwareFieldValueResolver;
3637
import org.slf4j.Logger;
3738
import org.slf4j.LoggerFactory;
3839

@@ -45,7 +46,7 @@
4546
import java.util.stream.Collectors;
4647

4748
public class HandlebarsEngineAdapter extends AbstractTemplatingEngineAdapter {
48-
final Logger LOGGER = LoggerFactory.getLogger(HandlebarsEngineAdapter.class);
49+
final Logger LOGGER = LoggerFactory.getLogger(HandlebarsEngineAdapter.class);
4950
private final String[] extensions = {"handlebars", "hbs"};
5051

5152
// We use this as a simple lookup for valid file name extensions. This adapter will inspect .mustache (built-in) and infer the relevant handlebars filename
@@ -73,36 +74,13 @@ public TemplateSource sourceAt(String location) {
7374
}
7475
};
7576

76-
// $ref: https://github.com/jknack/handlebars.java/issues/917
77-
var accessAwareFieldValueResolverInstance = new FieldValueResolver() {
78-
@Override
79-
protected Set<FieldWrapper> members(
80-
Class<?> clazz) {
81-
var members = super.members(clazz);
82-
return members.stream()
83-
.filter(fw -> isValidField(fw))
84-
.collect(Collectors.toSet());
85-
}
86-
87-
boolean isValidField(
88-
FieldWrapper fw) {
89-
if (fw instanceof AccessibleObject) {
90-
if (isUseSetAccessible(fw)) {
91-
return true;
92-
}
93-
return false;
94-
}
95-
return true;
96-
}
97-
};
98-
9977
Context context = Context
10078
.newBuilder(bundle)
10179
.resolver(
10280
MapValueResolver.INSTANCE,
10381
JavaBeanValueResolver.INSTANCE,
104-
accessAwareFieldValueResolverInstance,
105-
MethodValueResolver.INSTANCE)
82+
MethodValueResolver.INSTANCE,
83+
AccessAwareFieldValueResolver.INSTANCE)
10684
.build();
10785

10886
Handlebars handlebars = new Handlebars(loader);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.openapitools.codegen.templating.handlebars;
2+
3+
import com.github.jknack.handlebars.context.FieldValueResolver;
4+
5+
import java.lang.reflect.AccessibleObject;
6+
import java.util.Set;
7+
import java.util.stream.Collectors;
8+
9+
// $ref: https://github.com/jknack/handlebars.java/issues/917
10+
public class AccessAwareFieldValueResolver extends FieldValueResolver {
11+
12+
public static final AccessAwareFieldValueResolver INSTANCE = new AccessAwareFieldValueResolver();
13+
14+
@Override
15+
protected Set<FieldValueResolver.FieldWrapper> members(Class<?> clazz) {
16+
var members = super.members(clazz);
17+
return members.stream()
18+
.filter(this::isValidField)
19+
.collect(Collectors.toSet());
20+
}
21+
22+
boolean isValidField(FieldWrapper fw) {
23+
if (fw instanceof AccessibleObject) {
24+
return isUseSetAccessible(fw);
25+
}
26+
return true;
27+
}
28+
}

modules/openapi-generator/src/test/java/org/openapitools/codegen/templating/HandlebarsEngineAdapterTest.java

+75-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
package org.openapitools.codegen.templating;
22

3+
import org.mockito.Mockito;
4+
import org.openapitools.codegen.api.TemplatingExecutor;
35
import org.testng.annotations.DataProvider;
46
import org.testng.annotations.Test;
57

6-
import static org.testng.Assert.*;
8+
import java.io.IOException;
9+
import java.util.Map;
10+
11+
import static org.testng.Assert.assertEquals;
712

813
public class HandlebarsEngineAdapterTest {
914
@Test(dataProvider = "handlesFileExpectations")
@@ -33,4 +38,73 @@ public Object[][] handlesFileExpectations() {
3338
{"README.md", false, "Should not attempt to handle non-handlebars extensions (other than mustache)"}
3439
};
3540
}
41+
42+
@Test(description = "verify https://github.com/jknack/handlebars.java/issues/940#issue-1111612043 is fixed")
43+
public void testHandlePartialTemplate() throws IOException {
44+
// Given
45+
HandlebarsEngineAdapter adapter = new HandlebarsEngineAdapter();
46+
TemplatingExecutor executorMock = Mockito.mock(TemplatingExecutor.class);
47+
Mockito.when(executorMock.getFullTemplateContents("outerTemplate.hbs")).thenReturn("Contents: {{>innerTemplate}}");
48+
Mockito.when(executorMock.getFullTemplateContents("innerTemplate.hbs")).thenReturn("'Specific contents'");
49+
50+
// When
51+
String generatedFile = adapter.compileTemplate(executorMock, Map.of(), "outerTemplate.hbs");
52+
53+
// Then
54+
assertEquals(generatedFile, "Contents: 'Specific contents'");
55+
}
56+
57+
@Test(description = "should prioritize public getters over breaking encapsulation")
58+
public void testResolverPriority() throws IOException {
59+
// Given
60+
HandlebarsEngineAdapter adapter = new HandlebarsEngineAdapter();
61+
TemplatingExecutor executorMock = Mockito.mock(TemplatingExecutor.class);
62+
Mockito.when(executorMock.getFullTemplateContents("outerTemplate.hbs")).thenReturn(
63+
"Contents: {{#propertyObj}}" + "\n" +
64+
" public getter: {{valueMethodAndBean}}" + "\n" +
65+
" public method: {{valueAndMethod}}" + "\n" +
66+
" private property: {{valueOnly}}" + "{{/propertyObj}}");
67+
68+
Map<String, Object> bundle = Map.of("propertyObj", new PropertyObject());
69+
70+
// When
71+
String generatedFile = adapter.compileTemplate(executorMock, bundle, "outerTemplate.hbs");
72+
73+
// Then
74+
assertEquals(generatedFile, "Contents: \n" +
75+
" public getter: get_raw_data1_formatted\n" +
76+
" public method: raw_data2_formatted\n" +
77+
" private property: raw_data3");
78+
}
79+
80+
static class PropertyObject {
81+
/**
82+
* getter-exposed
83+
*/
84+
private final String valueMethodAndBean = "raw_data1";
85+
86+
public String valueMethodAndBean() {
87+
return valueMethodAndBean + "_formatted";
88+
}
89+
90+
public String getValueMethodAndBean() {
91+
return "get_" + valueMethodAndBean();
92+
}
93+
94+
/**
95+
* method-exposed
96+
*/
97+
private final String valueAndMethod = "raw_data2";
98+
99+
public String valueAndMethod() {
100+
return valueAndMethod + "_formatted";
101+
}
102+
103+
/**
104+
* private
105+
* note: ideally we long-term move towards respecting encapsulation where possible
106+
*/
107+
@SuppressWarnings({"unused", "java:S1068"}) // this private value is still read by our HandleBars engine
108+
private final String valueOnly = "raw_data3";
109+
}
36110
}

modules/openapi-generator/src/test/java/org/openapitools/codegen/templating/handlebars/StringHelpersTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ private void evaluate(HashMap<String, Object> data, String template, String expe
2121
Context context = Context
2222
.newBuilder(data)
2323
.resolver(
24-
FieldValueResolver.INSTANCE)
24+
AccessAwareFieldValueResolver.INSTANCE)
2525
.build();
2626

2727
Template tmpl = handlebars.compileInline(template);

0 commit comments

Comments
 (0)