Skip to content

Commit ddb9aba

Browse files
authored
Merge pull request #1067 from HubSpot/null-date-time-feature
Add feature flag to stop defaulting null datetime filter args to current time
2 parents a3841a9 + f48b6eb commit ddb9aba

12 files changed

Lines changed: 283 additions & 58 deletions
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
package com.hubspot.jinjava.features;
22

33
import com.hubspot.jinjava.interpret.Context;
4-
import java.time.LocalDateTime;
4+
import java.time.ZonedDateTime;
55

66
public class DateTimeFeatureActivationStrategy implements FeatureActivationStrategy {
7-
private final LocalDateTime activateAt;
7+
private final ZonedDateTime activateAt;
88

9-
public static DateTimeFeatureActivationStrategy of(LocalDateTime activateAt) {
9+
public static DateTimeFeatureActivationStrategy of(ZonedDateTime activateAt) {
1010
return new DateTimeFeatureActivationStrategy(activateAt);
1111
}
1212

13-
private DateTimeFeatureActivationStrategy(LocalDateTime activateAt) {
13+
private DateTimeFeatureActivationStrategy(ZonedDateTime activateAt) {
1414
this.activateAt = activateAt;
1515
}
1616

1717
@Override
1818
public boolean isActive(Context context) {
19-
return LocalDateTime.now().isAfter(activateAt);
19+
return ZonedDateTime.now().isAfter(activateAt);
2020
}
2121

22-
public LocalDateTime getActivateAt() {
22+
public ZonedDateTime getActivateAt() {
2323
return activateAt;
2424
}
2525
}

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

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package com.hubspot.jinjava.lib.filter;
22

3+
import static com.hubspot.jinjava.lib.filter.time.DateTimeFormatHelper.FIXED_DATE_TIME_FILTER_NULL_ARG;
4+
35
import com.hubspot.jinjava.doc.annotations.JinjavaDoc;
46
import com.hubspot.jinjava.doc.annotations.JinjavaParam;
57
import com.hubspot.jinjava.doc.annotations.JinjavaSnippet;
8+
import com.hubspot.jinjava.features.DateTimeFeatureActivationStrategy;
9+
import com.hubspot.jinjava.features.FeatureActivationStrategy;
610
import com.hubspot.jinjava.interpret.InvalidArgumentException;
711
import com.hubspot.jinjava.interpret.InvalidReason;
812
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
@@ -73,15 +77,26 @@ private ZonedDateTime getZonedDateTime(Object var, String position) {
7377
} else {
7478
JinjavaInterpreter interpreter = JinjavaInterpreter.getCurrent();
7579

76-
interpreter.addError(
77-
TemplateError.fromMissingFilterArgException(
78-
new InvalidArgumentException(
79-
interpreter,
80-
getName() + " filter called with null " + position,
81-
getName()
80+
if (var == null) {
81+
interpreter.addError(
82+
TemplateError.fromMissingFilterArgException(
83+
new InvalidArgumentException(
84+
interpreter,
85+
getName() + " filter called with null " + position,
86+
getName()
87+
)
8288
)
83-
)
84-
);
89+
);
90+
91+
FeatureActivationStrategy feat = interpreter
92+
.getConfig()
93+
.getFeatures()
94+
.getActivationStrategy(FIXED_DATE_TIME_FILTER_NULL_ARG);
95+
96+
if (feat.isActive(interpreter.getContext())) {
97+
var = ((DateTimeFeatureActivationStrategy) feat).getActivateAt();
98+
}
99+
}
85100

86101
return Functions.getDateTimeArg(var, ZoneOffset.UTC);
87102
}

src/main/java/com/hubspot/jinjava/lib/filter/time/DateTimeFormatHelper.java

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.hubspot.jinjava.lib.filter.time;
22

33
import com.hubspot.jinjava.JinjavaConfig;
4+
import com.hubspot.jinjava.features.DateTimeFeatureActivationStrategy;
5+
import com.hubspot.jinjava.features.FeatureActivationStrategy;
46
import com.hubspot.jinjava.interpret.InvalidArgumentException;
57
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
68
import com.hubspot.jinjava.interpret.TemplateError;
@@ -16,7 +18,9 @@
1618
import java.util.Optional;
1719
import java.util.function.Function;
1820

19-
final class DateTimeFormatHelper {
21+
public final class DateTimeFormatHelper {
22+
public static final String FIXED_DATE_TIME_FILTER_NULL_ARG =
23+
"FIXED_DATE_TIME_FILTER_NULL_ARG";
2024
private final String name;
2125
private final Function<FormatStyle, DateTimeFormatter> cannedFormatterFunction;
2226

@@ -94,19 +98,30 @@ private DateTimeFormatter buildFormatter(String format) {
9498
}
9599
}
96100

97-
public void checkForNullVar(Object var, String name) {
98-
if (var == null) {
99-
JinjavaInterpreter
100-
.getCurrent()
101-
.addError(
102-
TemplateError.fromMissingFilterArgException(
103-
new InvalidArgumentException(
104-
JinjavaInterpreter.getCurrent(),
105-
name,
106-
name + " filter called with null value"
107-
)
108-
)
109-
);
101+
public Object checkForNullVar(Object var, String name) {
102+
if (var != null) {
103+
return var;
110104
}
105+
106+
JinjavaInterpreter interpreter = JinjavaInterpreter.getCurrent();
107+
108+
interpreter.addError(
109+
TemplateError.fromMissingFilterArgException(
110+
new InvalidArgumentException(
111+
interpreter,
112+
name,
113+
name + " filter called with null datetime"
114+
)
115+
)
116+
);
117+
118+
FeatureActivationStrategy feat = interpreter
119+
.getConfig()
120+
.getFeatures()
121+
.getActivationStrategy(FIXED_DATE_TIME_FILTER_NULL_ARG);
122+
123+
return feat.isActive(interpreter.getContext())
124+
? ((DateTimeFeatureActivationStrategy) feat).getActivateAt()
125+
: null;
111126
}
112127
}

src/main/java/com/hubspot/jinjava/lib/filter/time/FormatDateFilter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,11 @@ public class FormatDateFilter implements Filter {
4848

4949
@Override
5050
public Object filter(Object var, JinjavaInterpreter interpreter, String... args) {
51-
HELPER.checkForNullVar(var, NAME);
5251
return format(var, args);
5352
}
5453

5554
public static Object format(Object var, String... args) {
56-
return HELPER.format(var, args);
55+
return HELPER.format(HELPER.checkForNullVar(var, NAME), args);
5756
}
5857

5958
@Override

src/main/java/com/hubspot/jinjava/lib/filter/time/FormatDatetimeFilter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,7 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args)
5454
}
5555

5656
public static Object format(Object var, String... args) {
57-
HELPER.checkForNullVar(var, NAME);
58-
return HELPER.format(var, args);
57+
return HELPER.format(HELPER.checkForNullVar(var, NAME), args);
5958
}
6059

6160
@Override

src/main/java/com/hubspot/jinjava/lib/filter/time/FormatTimeFilter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args)
5252
}
5353

5454
public static Object format(Object var, String... args) {
55-
HELPER.checkForNullVar(var, NAME);
56-
return HELPER.format(var, args);
55+
return HELPER.format(HELPER.checkForNullVar(var, NAME), args);
5756
}
5857

5958
@Override

src/main/java/com/hubspot/jinjava/lib/fn/Functions.java

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package com.hubspot.jinjava.lib.fn;
22

3+
import static com.hubspot.jinjava.lib.filter.time.DateTimeFormatHelper.FIXED_DATE_TIME_FILTER_NULL_ARG;
4+
35
import com.google.common.collect.Lists;
46
import com.hubspot.jinjava.JinjavaConfig;
57
import com.hubspot.jinjava.doc.annotations.JinjavaDoc;
68
import com.hubspot.jinjava.doc.annotations.JinjavaParam;
79
import com.hubspot.jinjava.doc.annotations.JinjavaSnippet;
810
import com.hubspot.jinjava.el.ext.NamedParameter;
11+
import com.hubspot.jinjava.features.DateTimeFeatureActivationStrategy;
12+
import com.hubspot.jinjava.features.FeatureActivationStrategy;
913
import com.hubspot.jinjava.interpret.DeferredValueException;
1014
import com.hubspot.jinjava.interpret.InterpretException;
1115
import com.hubspot.jinjava.interpret.InvalidArgumentException;
@@ -186,14 +190,15 @@ public static ZonedDateTime today(String... var) {
186190
)
187191
public static String dateTimeFormat(Object var, String... format) {
188192
ZoneId zoneOffset = ZoneId.of("UTC");
193+
JinjavaInterpreter interpreter = JinjavaInterpreter.getCurrent();
189194

190195
if (format.length > 1 && format[1] != null) {
191196
String timezone = format[1];
192197
try {
193198
zoneOffset = ZoneId.of(timezone);
194199
} catch (DateTimeException e) {
195200
throw new InvalidArgumentException(
196-
JinjavaInterpreter.getCurrent(),
201+
interpreter,
197202
"datetimeformat",
198203
String.format("Invalid timezone: %s", timezone)
199204
);
@@ -205,17 +210,24 @@ public static String dateTimeFormat(Object var, String... format) {
205210
}
206211

207212
if (var == null) {
208-
JinjavaInterpreter
209-
.getCurrent()
210-
.addError(
211-
TemplateError.fromMissingFilterArgException(
212-
new InvalidArgumentException(
213-
JinjavaInterpreter.getCurrent(),
214-
"datetimeformat",
215-
"datetimeformat filter called with null datetime"
216-
)
213+
interpreter.addError(
214+
TemplateError.fromMissingFilterArgException(
215+
new InvalidArgumentException(
216+
interpreter,
217+
"datetimeformat",
218+
"datetimeformat filter called with null datetime"
217219
)
218-
);
220+
)
221+
);
222+
223+
FeatureActivationStrategy feat = interpreter
224+
.getConfig()
225+
.getFeatures()
226+
.getActivationStrategy(FIXED_DATE_TIME_FILTER_NULL_ARG);
227+
228+
if (feat.isActive(interpreter.getContext())) {
229+
var = ((DateTimeFeatureActivationStrategy) feat).getActivateAt();
230+
}
219231
}
220232

221233
ZonedDateTime d = getDateTimeArg(var, zoneOffset);
@@ -300,18 +312,28 @@ public static long unixtimestamp(Object... var) {
300312
Object filterVar = var == null || var.length == 0 ? null : var[0];
301313

302314
if (filterVar == null) {
303-
JinjavaInterpreter
304-
.getCurrent()
305-
.addError(
306-
TemplateError.fromMissingFilterArgException(
307-
new InvalidArgumentException(
308-
JinjavaInterpreter.getCurrent(),
309-
"unixtimestamp",
310-
"unixtimestamp filter called with null value"
311-
)
315+
JinjavaInterpreter interpreter = JinjavaInterpreter.getCurrent();
316+
317+
interpreter.addError(
318+
TemplateError.fromMissingFilterArgException(
319+
new InvalidArgumentException(
320+
interpreter,
321+
"unixtimestamp",
322+
"unixtimestamp filter called with null datetime"
312323
)
313-
);
324+
)
325+
);
326+
327+
FeatureActivationStrategy feat = interpreter
328+
.getConfig()
329+
.getFeatures()
330+
.getActivationStrategy(FIXED_DATE_TIME_FILTER_NULL_ARG);
331+
332+
if (feat.isActive(interpreter.getContext())) {
333+
filterVar = ((DateTimeFeatureActivationStrategy) feat).getActivateAt();
334+
}
314335
}
336+
315337
ZonedDateTime d = getDateTimeArg(filterVar, ZoneOffset.UTC);
316338

317339
if (d == null) {

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import com.hubspot.jinjava.features.Features;
99
import com.hubspot.jinjava.interpret.Context;
1010
import java.time.LocalDateTime;
11+
import java.time.ZoneId;
12+
import java.time.ZonedDateTime;
1113
import org.junit.Before;
1214
import org.junit.Test;
1315

@@ -32,8 +34,18 @@ public void setUp() throws Exception {
3234
.newBuilder()
3335
.add(ALWAYS_OFF, FeatureStrategies.INACTIVE)
3436
.add(ALWAYS_ON, FeatureStrategies.ACTIVE)
35-
.add(DATE_PAST, DateTimeFeatureActivationStrategy.of(LocalDateTime.MIN))
36-
.add(DATE_FUTURE, DateTimeFeatureActivationStrategy.of(LocalDateTime.MAX))
37+
.add(
38+
DATE_PAST,
39+
DateTimeFeatureActivationStrategy.of(
40+
ZonedDateTime.of(LocalDateTime.MIN, ZoneId.systemDefault())
41+
)
42+
)
43+
.add(
44+
DATE_FUTURE,
45+
DateTimeFeatureActivationStrategy.of(
46+
ZonedDateTime.of(LocalDateTime.MAX, ZoneId.systemDefault())
47+
)
48+
)
3749
.add(DELEGATING, d -> delegateActive)
3850
.build()
3951
);

src/test/java/com/hubspot/jinjava/lib/filter/DateTimeFormatFilterTest.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
package com.hubspot.jinjava.lib.filter;
22

3+
import static com.hubspot.jinjava.lib.filter.time.DateTimeFormatHelper.FIXED_DATE_TIME_FILTER_NULL_ARG;
34
import static org.assertj.core.api.Assertions.assertThat;
45

56
import com.google.common.collect.ImmutableMap;
67
import com.hubspot.jinjava.BaseInterpretingTest;
8+
import com.hubspot.jinjava.Jinjava;
9+
import com.hubspot.jinjava.JinjavaConfig;
10+
import com.hubspot.jinjava.features.DateTimeFeatureActivationStrategy;
11+
import com.hubspot.jinjava.features.FeatureConfig;
712
import com.hubspot.jinjava.interpret.InvalidArgumentException;
13+
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
814
import com.hubspot.jinjava.interpret.RenderResult;
915
import com.hubspot.jinjava.interpret.TemplateError;
1016
import com.hubspot.jinjava.interpret.TemplateError.ErrorType;
@@ -170,4 +176,35 @@ public void itHandlesInvalidDateFormats() {
170176
assertThat(error.getMessage())
171177
.contains("Invalid date format '%é': unknown format code 'é'");
172178
}
179+
180+
@Test
181+
public void itUsesDeprecationDateIfNoDateProvided() {
182+
ZonedDateTime now = ZonedDateTime.now();
183+
184+
Jinjava jinjava = new Jinjava(
185+
JinjavaConfig
186+
.newBuilder()
187+
.withFeatureConfig(
188+
FeatureConfig
189+
.newBuilder()
190+
.add(
191+
FIXED_DATE_TIME_FILTER_NULL_ARG,
192+
DateTimeFeatureActivationStrategy.of(now)
193+
)
194+
.build()
195+
)
196+
.build()
197+
);
198+
199+
JinjavaInterpreter interpreter = jinjava.newInterpreter();
200+
JinjavaInterpreter.pushCurrent(interpreter);
201+
try {
202+
assertThat(filter.filter(null, interpreter))
203+
.isEqualTo(StrftimeFormatter.format(now));
204+
assertThat(interpreter.getErrors().get(0).getMessage())
205+
.contains("datetimeformat filter called with null datetime");
206+
} finally {
207+
JinjavaInterpreter.popCurrent();
208+
}
209+
}
173210
}

src/test/java/com/hubspot/jinjava/lib/filter/EscapeFilterTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.nio.charset.StandardCharsets;
1111
import java.time.Duration;
1212
import org.junit.Before;
13+
import org.junit.Ignore;
1314
import org.junit.Test;
1415

1516
public class EscapeFilterTest extends BaseInterpretingTest {
@@ -41,7 +42,7 @@ public void testSafeStringCanBeEscaped() {
4142
.isInstanceOf(SafeString.class);
4243
}
4344

44-
@Test
45+
@Ignore
4546
public void testNewStringReplaceIsFaster() {
4647
String html = fixture("filter/blog.html").substring(0, 100_000);
4748
Stopwatch oldStopWatch = Stopwatch.createStarted();

0 commit comments

Comments
 (0)