Skip to content

Commit a3841a9

Browse files
authored
Merge pull request #1066 from HubSpot/jboulter-add-features-flags-to-jinjava-config
2 parents 055f189 + 3c9c0f6 commit a3841a9

7 files changed

Lines changed: 180 additions & 0 deletions

File tree

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import com.hubspot.jinjava.el.JinjavaObjectUnwrapper;
2424
import com.hubspot.jinjava.el.JinjavaProcessors;
2525
import com.hubspot.jinjava.el.ObjectUnwrapper;
26+
import com.hubspot.jinjava.features.FeatureConfig;
27+
import com.hubspot.jinjava.features.Features;
2628
import com.hubspot.jinjava.interpret.Context;
2729
import com.hubspot.jinjava.interpret.Context.Library;
2830
import com.hubspot.jinjava.interpret.InterpreterFactory;
@@ -80,6 +82,8 @@ public class JinjavaConfig {
8082
private final boolean enablePreciseDivideFilter;
8183
private final ObjectMapper objectMapper;
8284

85+
private final Features features;
86+
8387
private final ObjectUnwrapper objectUnwrapper;
8488
private final JinjavaProcessors processors;
8589

@@ -140,6 +144,7 @@ private JinjavaConfig(Builder builder) {
140144
objectMapper = setupObjectMapper(builder.objectMapper);
141145
objectUnwrapper = builder.objectUnwrapper;
142146
processors = builder.processors;
147+
features = new Features(builder.featureConfig);
143148
}
144149

145150
private ObjectMapper setupObjectMapper(@Nullable ObjectMapper objectMapper) {
@@ -288,6 +293,10 @@ public DateTimeProvider getDateTimeProvider() {
288293
return dateTimeProvider;
289294
}
290295

296+
public Features getFeatures() {
297+
return features;
298+
}
299+
291300
public static class Builder {
292301
private Charset charset = StandardCharsets.UTF_8;
293302
private Locale locale = Locale.ENGLISH;
@@ -322,6 +331,7 @@ public static class Builder {
322331

323332
private ObjectUnwrapper objectUnwrapper = new JinjavaObjectUnwrapper();
324333
private JinjavaProcessors processors = JinjavaProcessors.newBuilder().build();
334+
private FeatureConfig featureConfig = FeatureConfig.newBuilder().build();
325335

326336
private Builder() {}
327337

@@ -508,6 +518,11 @@ public Builder withProcessors(JinjavaProcessors jinjavaProcessors) {
508518
return this;
509519
}
510520

521+
public Builder withFeatureConfig(FeatureConfig featureConfig) {
522+
this.featureConfig = featureConfig;
523+
return this;
524+
}
525+
511526
public JinjavaConfig build() {
512527
return new JinjavaConfig(this);
513528
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.hubspot.jinjava.features;
2+
3+
import com.hubspot.jinjava.interpret.Context;
4+
import java.time.LocalDateTime;
5+
6+
public class DateTimeFeatureActivationStrategy implements FeatureActivationStrategy {
7+
private final LocalDateTime activateAt;
8+
9+
public static DateTimeFeatureActivationStrategy of(LocalDateTime activateAt) {
10+
return new DateTimeFeatureActivationStrategy(activateAt);
11+
}
12+
13+
private DateTimeFeatureActivationStrategy(LocalDateTime activateAt) {
14+
this.activateAt = activateAt;
15+
}
16+
17+
@Override
18+
public boolean isActive(Context context) {
19+
return LocalDateTime.now().isAfter(activateAt);
20+
}
21+
22+
public LocalDateTime getActivateAt() {
23+
return activateAt;
24+
}
25+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.hubspot.jinjava.features;
2+
3+
import com.hubspot.jinjava.interpret.Context;
4+
5+
public interface FeatureActivationStrategy {
6+
boolean isActive(Context context);
7+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.hubspot.jinjava.features;
2+
3+
import com.google.common.collect.ImmutableMap;
4+
import java.util.HashMap;
5+
import java.util.Map;
6+
7+
public class FeatureConfig {
8+
Map<String, FeatureActivationStrategy> features;
9+
10+
private FeatureConfig(Map<String, FeatureActivationStrategy> features) {
11+
this.features = ImmutableMap.copyOf(features);
12+
}
13+
14+
public FeatureActivationStrategy getFeature(String name) {
15+
return features.getOrDefault(name, FeatureStrategies.INACTIVE);
16+
}
17+
18+
public static FeatureConfig.Builder newBuilder() {
19+
return new Builder();
20+
}
21+
22+
public static class Builder {
23+
private final Map<String, FeatureActivationStrategy> features = new HashMap<>();
24+
25+
public Builder add(String name, FeatureActivationStrategy strategy) {
26+
features.put(name, strategy);
27+
return this;
28+
}
29+
30+
public FeatureConfig build() {
31+
return new FeatureConfig(features);
32+
}
33+
}
34+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.hubspot.jinjava.features;
2+
3+
public class FeatureStrategies {
4+
public static final FeatureActivationStrategy INACTIVE = c -> false;
5+
public static final FeatureActivationStrategy ACTIVE = c -> true;
6+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.hubspot.jinjava.features;
2+
3+
import com.hubspot.jinjava.interpret.Context;
4+
5+
public class Features {
6+
private final FeatureConfig featureConfig;
7+
8+
public Features(FeatureConfig featureConfig) {
9+
this.featureConfig = featureConfig;
10+
}
11+
12+
public boolean isActive(String featureName, Context context) {
13+
return getActivationStrategy(featureName).isActive(context);
14+
}
15+
16+
public FeatureActivationStrategy getActivationStrategy(String featureName) {
17+
return featureConfig.getFeature(featureName);
18+
}
19+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package com.hubspot.jinjava;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import com.hubspot.jinjava.features.DateTimeFeatureActivationStrategy;
6+
import com.hubspot.jinjava.features.FeatureConfig;
7+
import com.hubspot.jinjava.features.FeatureStrategies;
8+
import com.hubspot.jinjava.features.Features;
9+
import com.hubspot.jinjava.interpret.Context;
10+
import java.time.LocalDateTime;
11+
import org.junit.Before;
12+
import org.junit.Test;
13+
14+
public class FeaturesTest {
15+
private static final String ALWAYS_OFF = "alwaysOff";
16+
private static final String ALWAYS_ON = "alwaysOn";
17+
private static final String DATE_PAST = "datePast";
18+
private static final String DATE_FUTURE = "dateFuture";
19+
private static final String DELEGATING = "delegating";
20+
21+
private Features features;
22+
23+
private boolean delegateActive = false;
24+
25+
private Context context = new Context();
26+
27+
@Before
28+
public void setUp() throws Exception {
29+
features =
30+
new Features(
31+
FeatureConfig
32+
.newBuilder()
33+
.add(ALWAYS_OFF, FeatureStrategies.INACTIVE)
34+
.add(ALWAYS_ON, FeatureStrategies.ACTIVE)
35+
.add(DATE_PAST, DateTimeFeatureActivationStrategy.of(LocalDateTime.MIN))
36+
.add(DATE_FUTURE, DateTimeFeatureActivationStrategy.of(LocalDateTime.MAX))
37+
.add(DELEGATING, d -> delegateActive)
38+
.build()
39+
);
40+
}
41+
42+
@Test
43+
public void itHasEnabledFeature() {
44+
assertThat(features.isActive(ALWAYS_ON, context)).isTrue();
45+
}
46+
47+
@Test
48+
public void itDoesNotHaveDisabledFeature() {
49+
assertThat(features.isActive(ALWAYS_OFF, context)).isFalse();
50+
}
51+
52+
@Test
53+
public void itHasPastEnabledFeature() {
54+
assertThat(features.isActive(DATE_PAST, context)).isTrue();
55+
}
56+
57+
@Test
58+
public void itDoesNotHaveFutureEnabledFeature() {
59+
assertThat(features.isActive(DATE_FUTURE, context)).isFalse();
60+
}
61+
62+
@Test
63+
public void itUsesDelegate() {
64+
delegateActive = false;
65+
assertThat(features.isActive(DELEGATING, context)).isEqualTo(delegateActive);
66+
delegateActive = true;
67+
assertThat(features.isActive(DELEGATING, context)).isEqualTo(delegateActive);
68+
}
69+
70+
@Test
71+
public void itDefaultsToFalse() {
72+
assertThat(features.isActive("unknown", context)).isFalse();
73+
}
74+
}

0 commit comments

Comments
 (0)