Skip to content

Commit 99285f2

Browse files
committed
add features flags to jinjava config
1 parent 055f189 commit 99285f2

9 files changed

Lines changed: 215 additions & 0 deletions

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

Lines changed: 11 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) {
@@ -322,6 +327,7 @@ public static class Builder {
322327

323328
private ObjectUnwrapper objectUnwrapper = new JinjavaObjectUnwrapper();
324329
private JinjavaProcessors processors = JinjavaProcessors.newBuilder().build();
330+
private FeatureConfig featureConfig = FeatureConfig.newBuilder().build();
325331

326332
private Builder() {}
327333

@@ -508,6 +514,11 @@ public Builder withProcessors(JinjavaProcessors jinjavaProcessors) {
508514
return this;
509515
}
510516

517+
public Builder withFeatureConfig(FeatureConfig featureConfig) {
518+
this.featureConfig = featureConfig;
519+
return this;
520+
}
521+
511522
public JinjavaConfig build() {
512523
return new JinjavaConfig(this);
513524
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.hubspot.jinjava.features;
2+
3+
import java.time.LocalDateTime;
4+
5+
public class DateTimeFeatureActivationStrategy implements FeatureActivationStrategy {
6+
private final LocalDateTime activateAt;
7+
8+
public static DateTimeFeatureActivationStrategy of(LocalDateTime activateAt) {
9+
return new DateTimeFeatureActivationStrategy(activateAt);
10+
}
11+
12+
private DateTimeFeatureActivationStrategy(LocalDateTime activateAt) {
13+
this.activateAt = activateAt;
14+
}
15+
16+
@Override
17+
public boolean isActive() {
18+
return LocalDateTime.now().isAfter(activateAt);
19+
}
20+
21+
public LocalDateTime getActivateAt() {
22+
return activateAt;
23+
}
24+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.hubspot.jinjava.features;
2+
3+
import java.util.function.Supplier;
4+
5+
public class DelegatingFeatureActivationStrategy implements FeatureActivationStrategy {
6+
public Supplier<Boolean> delegate;
7+
8+
public static FeatureActivationStrategy of(Supplier<Boolean> delegate) {
9+
return new DelegatingFeatureActivationStrategy(delegate);
10+
}
11+
12+
private DelegatingFeatureActivationStrategy(Supplier<Boolean> delegate) {
13+
this.delegate = delegate;
14+
}
15+
16+
@Override
17+
public boolean isActive() {
18+
return delegate.get();
19+
}
20+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.hubspot.jinjava.features;
2+
3+
public class DisabledFeatureActivationStrategy implements FeatureActivationStrategy {
4+
private static final DisabledFeatureActivationStrategy INSTANCE = new DisabledFeatureActivationStrategy();
5+
6+
@Override
7+
public boolean isActive() {
8+
return false;
9+
}
10+
11+
public static DisabledFeatureActivationStrategy getInstance() {
12+
return INSTANCE;
13+
}
14+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.hubspot.jinjava.features;
2+
3+
public interface FeatureActivationStrategy {
4+
boolean isActive();
5+
}
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, DisabledFeatureActivationStrategy.getInstance());
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: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.hubspot.jinjava.features;
2+
3+
public class Features {
4+
private final FeatureConfig featureConfig;
5+
6+
public Features(FeatureConfig featureConfig) {
7+
this.featureConfig = featureConfig;
8+
}
9+
10+
public boolean isActive(String featureName) {
11+
return getActivationStrategy(featureName).isActive();
12+
}
13+
14+
public FeatureActivationStrategy getActivationStrategy(String featureName) {
15+
return featureConfig.getFeature(featureName);
16+
}
17+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.hubspot.jinjava.features;
2+
3+
public class StaticFeatureActivationStrategy implements FeatureActivationStrategy {
4+
private final boolean active;
5+
6+
public static StaticFeatureActivationStrategy of(boolean active) {
7+
return new StaticFeatureActivationStrategy(active);
8+
}
9+
10+
private StaticFeatureActivationStrategy(boolean active) {
11+
this.active = active;
12+
}
13+
14+
@Override
15+
public boolean isActive() {
16+
return active;
17+
}
18+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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.DelegatingFeatureActivationStrategy;
7+
import com.hubspot.jinjava.features.FeatureConfig;
8+
import com.hubspot.jinjava.features.Features;
9+
import com.hubspot.jinjava.features.StaticFeatureActivationStrategy;
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+
@Before
26+
public void setUp() throws Exception {
27+
features =
28+
new Features(
29+
FeatureConfig
30+
.newBuilder()
31+
.add(ALWAYS_OFF, StaticFeatureActivationStrategy.of(false))
32+
.add(ALWAYS_ON, StaticFeatureActivationStrategy.of(true))
33+
.add(DATE_PAST, DateTimeFeatureActivationStrategy.of(LocalDateTime.MIN))
34+
.add(DATE_FUTURE, DateTimeFeatureActivationStrategy.of(LocalDateTime.MAX))
35+
.add(DELEGATING, DelegatingFeatureActivationStrategy.of(() -> delegateActive))
36+
.build()
37+
);
38+
}
39+
40+
@Test
41+
public void itHasEnabledFeature() {
42+
assertThat(features.isActive(ALWAYS_ON)).isTrue();
43+
}
44+
45+
@Test
46+
public void itHasDisabledFeature() {
47+
assertThat(features.isActive(ALWAYS_OFF)).isFalse();
48+
}
49+
50+
@Test
51+
public void itHasPastEnabledFeature() {
52+
assertThat(features.isActive(DATE_PAST)).isTrue();
53+
}
54+
55+
@Test
56+
public void itHasFutureEnabledFeature() {
57+
assertThat(features.isActive(DATE_FUTURE)).isFalse();
58+
}
59+
60+
@Test
61+
public void itUsesDelegate() {
62+
delegateActive = false;
63+
assertThat(features.isActive(DELEGATING)).isEqualTo(delegateActive);
64+
delegateActive = true;
65+
assertThat(features.isActive(DELEGATING)).isEqualTo(delegateActive);
66+
}
67+
68+
@Test
69+
public void itDefaultsToFalse() {
70+
assertThat(features.isActive("unknown")).isFalse();
71+
}
72+
}

0 commit comments

Comments
 (0)