Skip to content

Commit b144bb0

Browse files
fix: Preserve native types for multivariate feature option values (#205)
1 parent b39a67e commit b144bb0

File tree

2 files changed

+92
-3
lines changed

2 files changed

+92
-3
lines changed

src/main/java/com/flagsmith/models/features/MultivariateFeatureOptionModel.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55

66
@Data
77
public class MultivariateFeatureOptionModel extends BaseModel {
8-
private String value;
8+
private Object value;
99
}

src/test/java/com/flagsmith/flagengine/unit/mappers/EngineMappersTest.java

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
11
package com.flagsmith.flagengine.unit.mappers;
22

3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.flagsmith.MapperFactory;
5+
import com.flagsmith.flagengine.EvaluationContext;
6+
import com.flagsmith.flagengine.FeatureContext;
7+
import com.flagsmith.flagengine.FeatureValue;
8+
import com.flagsmith.flagengine.Traits;
39
import com.flagsmith.mappers.EngineMappers;
410
import com.flagsmith.models.TraitConfig;
11+
import com.flagsmith.models.environments.EnvironmentModel;
512

613
import static org.junit.jupiter.api.Assertions.assertEquals;
14+
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
715

16+
import java.util.List;
817
import java.util.Map;
918
import java.util.stream.Stream;
1019

20+
import org.junit.jupiter.api.Test;
1121
import org.junit.jupiter.params.ParameterizedTest;
1222
import org.junit.jupiter.params.provider.Arguments;
1323
import org.junit.jupiter.params.provider.MethodSource;
1424

1525
import com.flagsmith.FlagsmithTestHelper;
16-
import com.flagsmith.flagengine.EvaluationContext;
17-
import com.flagsmith.flagengine.Traits;
1826

1927
public class EngineMappersTest {
2028
private static Stream<Arguments> expectedTraitMaps() {
@@ -49,4 +57,85 @@ public void testMapContextAndIdentityDataToContext_returnsExpectedContext(
4957
expectedTraits.getAdditionalProperties(),
5058
mappedContext.getIdentity().getTraits().getAdditionalProperties());
5159
}
60+
61+
@Test
62+
public void testMapEnvironmentToContext_preservesMultivariateValueTypes()
63+
throws JsonProcessingException {
64+
String environmentJson = "{\n"
65+
+ " \"api_key\": \"test-key\",\n"
66+
+ " \"name\": \"Test\",\n"
67+
+ " \"project\": {\n"
68+
+ " \"name\": \"Test project\",\n"
69+
+ " \"organisation\": {\n"
70+
+ " \"feature_analytics\": false,\n"
71+
+ " \"name\": \"Test Org\",\n"
72+
+ " \"id\": 1,\n"
73+
+ " \"persist_trait_data\": true,\n"
74+
+ " \"stop_serving_flags\": false\n"
75+
+ " },\n"
76+
+ " \"id\": 1,\n"
77+
+ " \"hide_disabled_flags\": false,\n"
78+
+ " \"segments\": []\n"
79+
+ " },\n"
80+
+ " \"segment_overrides\": [],\n"
81+
+ " \"id\": 1,\n"
82+
+ " \"feature_states\": [\n"
83+
+ " {\n"
84+
+ " \"feature_state_value\": true,\n"
85+
+ " \"django_id\": 1,\n"
86+
+ " \"featurestate_uuid\": \"40eb539d-3713-4720-bbd4-829dbef10d51\",\n"
87+
+ " \"feature\": { \"name\": \"mv_feature\", \"type\": \"MULTIVARIATE\", \"id\": 1 },\n"
88+
+ " \"enabled\": true,\n"
89+
+ " \"multivariate_feature_state_values\": [\n"
90+
+ " {\n"
91+
+ " \"id\": 1,\n"
92+
+ " \"multivariate_feature_option\": { \"value\": false },\n"
93+
+ " \"percentage_allocation\": 50.0,\n"
94+
+ " \"mv_fs_value_uuid\": \"808cba14-03ca-4835-a7f7-58387f01f87d\"\n"
95+
+ " },\n"
96+
+ " {\n"
97+
+ " \"id\": 2,\n"
98+
+ " \"multivariate_feature_option\": { \"value\": 42 },\n"
99+
+ " \"percentage_allocation\": 30.0,\n"
100+
+ " \"mv_fs_value_uuid\": \"918dbb25-14db-4946-b8a8-69488f02f98e\"\n"
101+
+ " },\n"
102+
+ " {\n"
103+
+ " \"id\": 3,\n"
104+
+ " \"multivariate_feature_option\": { \"value\": \"a string\" },\n"
105+
+ " \"percentage_allocation\": 20.0,\n"
106+
+ " \"mv_fs_value_uuid\": \"a29eca36-25dc-5057-c9b9-7a599f13g09f\"\n"
107+
+ " }\n"
108+
+ " ]\n"
109+
+ " }\n"
110+
+ " ],\n"
111+
+ " \"identity_overrides\": []\n"
112+
+ "}";
113+
114+
EnvironmentModel env = MapperFactory.getMapper()
115+
.readValue(environmentJson, EnvironmentModel.class);
116+
117+
EvaluationContext context = EngineMappers.mapEnvironmentToContext(env);
118+
119+
FeatureContext feature = (FeatureContext) context.getFeatures()
120+
.getAdditionalProperties().get("mv_feature");
121+
List<FeatureValue> variants = feature.getVariants();
122+
123+
assertEquals(3, variants.size());
124+
125+
assertInstanceOf(Boolean.class, feature.getValue(),
126+
"Control value should be Boolean, not " + feature.getValue().getClass().getName());
127+
assertEquals(true, feature.getValue());
128+
129+
assertInstanceOf(Boolean.class, variants.get(0).getValue(),
130+
"Boolean variant value should be Boolean, not " + variants.get(0).getValue().getClass().getName());
131+
assertEquals(false, variants.get(0).getValue());
132+
133+
assertInstanceOf(Integer.class, variants.get(1).getValue(),
134+
"Integer variant value should be Integer, not " + variants.get(1).getValue().getClass().getName());
135+
assertEquals(42, variants.get(1).getValue());
136+
137+
assertInstanceOf(String.class, variants.get(2).getValue(),
138+
"String variant value should be String, not " + variants.get(2).getValue().getClass().getName());
139+
assertEquals("a string", variants.get(2).getValue());
140+
}
52141
}

0 commit comments

Comments
 (0)