Skip to content

Commit e4c7edf

Browse files
committed
Extract test objects to separate classes. Allow arrays. Add
AnnotationIntrospector
1 parent cb4e3aa commit e4c7edf

50 files changed

Lines changed: 1122 additions & 867 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/main/java/com/hubspot/jinjava/LegacyOverrides.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
@ImmutableSetEncodingEnabled
1111
@ImmutableListEncodingEnabled
1212
@ImmutableMapEncodingEnabled
13-
public interface LegacyOverrides {
13+
public interface LegacyOverrides extends WithLegacyOverrides {
1414
LegacyOverrides NONE = new Builder().build();
1515
LegacyOverrides THREE_POINT_0 = new Builder()
1616
.withEvaluateMapKeys(true)

src/main/java/com/hubspot/jinjava/el/ext/AllowlistGroup.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
import com.google.common.collect.ForwardingList;
55
import com.google.common.collect.ForwardingMap;
66
import com.google.common.collect.ForwardingSet;
7+
import com.hubspot.jinjava.interpret.NullValue;
78
import com.hubspot.jinjava.lib.exptest.ExpTest;
89
import com.hubspot.jinjava.lib.filter.Filter;
10+
import com.hubspot.jinjava.lib.fn.MacroFunction;
11+
import com.hubspot.jinjava.lib.fn.eager.EagerMacroFunction;
912
import com.hubspot.jinjava.objects.DummyObject;
1013
import com.hubspot.jinjava.objects.Namespace;
1114
import com.hubspot.jinjava.objects.SafeString;
@@ -71,6 +74,7 @@ String[] allowedDeclaredMethodsFromClasses() {
7174
DummyObject.class.getCanonicalName(),
7275
Namespace.class.getCanonicalName(),
7376
SafeString.class.getCanonicalName(),
77+
NullValue.class.getCanonicalName(),
7478
};
7579

7680
@Override
@@ -112,9 +116,18 @@ String[] allowedReturnTypeClasses() {
112116
String[] allowedDeclaredMethodsFromClasses() {
113117
return ARRAY;
114118
}
119+
120+
@Override
121+
boolean enableArrays() {
122+
return true;
123+
}
115124
},
116125
JinjavaTagConstructs {
117-
private static final String[] ARRAY = { ForLoop.class.getCanonicalName() };
126+
private static final String[] ARRAY = {
127+
ForLoop.class.getCanonicalName(),
128+
MacroFunction.class.getCanonicalName(),
129+
EagerMacroFunction.class.getCanonicalName(),
130+
};
118131

119132
@Override
120133
String[] allowedReturnTypeClasses() {
@@ -173,4 +186,8 @@ String[] allowedReturnTypeCanonicalClassPrefixes() {
173186
String[] allowedReturnTypeClasses() {
174187
return new String[0];
175188
}
189+
190+
boolean enableArrays() {
191+
return false;
192+
}
176193
}

src/main/java/com/hubspot/jinjava/el/ext/AllowlistReturnTypeValidator.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public final class AllowlistReturnTypeValidator {
1010

1111
private final ImmutableSet<String> allowedCanonicalClassPrefixes;
1212
private final ImmutableSet<String> allowedCanonicalClassNames;
13+
private final boolean allowArrays;
1314
private final ImmutableList<ReturnTypeValidator> additionalValidators;
1415

1516
public static AllowlistReturnTypeValidator create(
@@ -30,6 +31,7 @@ private AllowlistReturnTypeValidator(
3031
returnTypeValidatorConfig.allowedCanonicalClassPrefixes();
3132
this.allowedCanonicalClassNames =
3233
returnTypeValidatorConfig.allowedCanonicalClassNames();
34+
this.allowArrays = returnTypeValidatorConfig.allowArrays();
3335
this.additionalValidators = additionalValidators;
3436
this.allowedReturnTypesCache = new ConcurrentHashMap<>();
3537
}
@@ -39,8 +41,8 @@ public Object validateReturnType(Object o) {
3941
return null;
4042
}
4143
Class<?> clazz = o.getClass();
42-
while (clazz.isArray()) {
43-
clazz = clazz.getComponentType();
44+
if (clazz.isArray() && allowArrays) {
45+
return o;
4446
}
4547
String canonicalClassName = clazz.getCanonicalName();
4648
boolean isAllowedClassName = allowedReturnTypesCache.computeIfAbsent(
@@ -60,4 +62,17 @@ public Object validateReturnType(Object o) {
6062
}
6163
return o;
6264
}
65+
66+
public boolean allowReturnTypeClass(Class<?> clazz) {
67+
if (clazz.isArray() && allowArrays) {
68+
return true;
69+
}
70+
String canonicalClassName = clazz.getCanonicalName();
71+
return allowedReturnTypesCache.computeIfAbsent(
72+
canonicalClassName,
73+
c ->
74+
allowedCanonicalClassNames.contains(canonicalClassName) ||
75+
allowedCanonicalClassPrefixes.stream().anyMatch(canonicalClassName::startsWith)
76+
);
77+
}
6378
}

src/main/java/com/hubspot/jinjava/el/ext/ReturnTypeValidatorConfig.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ public abstract class ReturnTypeValidatorConfig {
3030

3131
public abstract ImmutableSet<String> allowedCanonicalClassNames();
3232

33+
@Value.Default
34+
public boolean allowArrays() {
35+
return false;
36+
}
37+
3338
@Value.Check
3439
void banClassesAndMethods() {
3540
if (
@@ -75,6 +80,9 @@ public Builder addAllowlistGroups(AllowlistGroup... allowlistGroups) {
7580
allowlistGroup.allowedReturnTypeCanonicalClassPrefixes()
7681
)
7782
.addAllowedCanonicalClassNames(allowlistGroup.allowedReturnTypeClasses());
83+
if (allowlistGroup.enableArrays()) {
84+
this.setAllowArrays(true);
85+
}
7886
}
7987
return this;
8088
}

src/main/java/com/hubspot/jinjava/lib/filter/ReverseFilter.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.hubspot.jinjava.doc.annotations.JinjavaParam;
2020
import com.hubspot.jinjava.doc.annotations.JinjavaSnippet;
2121
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
22+
import com.hubspot.jinjava.objects.collections.ArrayBacked;
2223
import java.util.Collection;
2324
import java.util.Iterator;
2425
import java.util.NoSuchElementException;
@@ -75,7 +76,7 @@ public String getName() {
7576
return "reverse";
7677
}
7778

78-
static class ReverseArrayIterator<T> implements Iterator<T> {
79+
static class ReverseArrayIterator<T> implements Iterator<T>, ArrayBacked<T> {
7980

8081
private final T[] array;
8182
private int index;
@@ -101,5 +102,10 @@ public T next() {
101102
public boolean hasNext() {
102103
return index >= 0;
103104
}
105+
106+
@Override
107+
public T[] backingArray() {
108+
return array;
109+
}
104110
}
105111
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.hubspot.jinjava.objects.collections;
2+
3+
public interface ArrayBacked<T> {
4+
T[] backingArray();
5+
}

src/main/java/com/hubspot/jinjava/objects/serialization/PyishObjectMapper.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import com.fasterxml.jackson.databind.ObjectWriter;
99
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
1010
import com.fasterxml.jackson.databind.SerializerProvider;
11+
import com.fasterxml.jackson.databind.introspect.Annotated;
12+
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
1113
import com.fasterxml.jackson.databind.module.SimpleModule;
1214
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
1315
import com.google.common.annotations.Beta;
@@ -53,6 +55,25 @@ private static ObjectMapper getPyishObjectMapper() {
5355
.addSerializer(PyishSerializable.class, PyishSerializer.INSTANCE)
5456
);
5557
mapper.getSerializerProvider().setNullKeySerializer(new NullKeySerializer());
58+
mapper.setAnnotationIntrospector(
59+
new JacksonAnnotationIntrospector() {
60+
@Override
61+
protected boolean _isIgnorable(Annotated a) {
62+
return (
63+
super._isIgnorable(a) ||
64+
!JinjavaInterpreter
65+
.getCurrentMaybe()
66+
.map(interpreter ->
67+
interpreter
68+
.getConfig()
69+
.getReturnTypeValidator()
70+
.allowReturnTypeClass(a.getRawType())
71+
)
72+
.orElse(false)
73+
);
74+
}
75+
}
76+
);
5677
return mapper;
5778
}
5879

src/main/java/com/hubspot/jinjava/util/EagerExpressionResolver.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import com.hubspot.jinjava.interpret.PartiallyDeferredValue;
1515
import com.hubspot.jinjava.interpret.TemplateSyntaxException;
1616
import com.hubspot.jinjava.interpret.UnknownTokenException;
17+
import com.hubspot.jinjava.objects.collections.ArrayBacked;
1718
import com.hubspot.jinjava.objects.serialization.PyishObjectMapper;
1819
import com.hubspot.jinjava.objects.serialization.PyishSerializable;
1920
import com.hubspot.jinjava.tree.ExpressionNode;
@@ -325,6 +326,9 @@ private static boolean isResolvableObjectRec(
325326
if (isPrimitive(val)) {
326327
return true;
327328
}
329+
if (val instanceof ArrayBacked<?> arrayBacked) {
330+
val = arrayBacked.backingArray();
331+
}
328332
try {
329333
if (val instanceof Collection || val instanceof Map) {
330334
int size = val instanceof Collection

src/test/java/com/hubspot/jinjava/FilterOverrideTest.java

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,22 @@
22

33
import static org.assertj.core.api.Assertions.assertThat;
44

5-
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
6-
import com.hubspot.jinjava.lib.filter.Filter;
5+
import com.hubspot.jinjava.testobjects.FilterOverrideTestObjects;
76
import java.util.HashMap;
87
import org.junit.Test;
98

109
public class FilterOverrideTest {
1110

1211
@Test
1312
public void itAllowsUsersToOverrideBuiltInFilters() {
14-
Jinjava jinjava = new Jinjava();
13+
Jinjava jinjava = new Jinjava(BaseJinjavaTest.newConfigBuilder().build());
1514
String template = "{{ 5 | add(6) }}";
1615

1716
assertThat(jinjava.render(template, new HashMap<>())).isEqualTo("11");
1817

19-
jinjava.getGlobalContext().registerClasses(DescriptiveAddFilter.class);
18+
jinjava
19+
.getGlobalContext()
20+
.registerClasses(FilterOverrideTestObjects.DescriptiveAddFilter.class);
2021
assertThat(jinjava.render(template, new HashMap<>())).isEqualTo("5 + 6 = 11");
2122
}
22-
23-
public static class DescriptiveAddFilter implements Filter {
24-
25-
@Override
26-
public String getName() {
27-
return "add";
28-
}
29-
30-
@Override
31-
public Object filter(Object var, JinjavaInterpreter interpreter, String... args) {
32-
return (
33-
var +
34-
" + " +
35-
args[0] +
36-
" = " +
37-
(Integer.parseInt(var.toString()) + Integer.parseInt(args[0]))
38-
);
39-
}
40-
}
4123
}

0 commit comments

Comments
 (0)