Skip to content

Commit 9be83e6

Browse files
authored
Merge pull request #8 from featbit/feature/v1.0.5
feature/v1.0.5: v1.0.5
2 parents 5a34b81 + 7a7497c commit 9be83e6

22 files changed

+723
-521
lines changed

README.md

+7-4
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ install the sdk in using maven
4545
<dependency>
4646
<groupId>co.featbit</groupId>
4747
<artifactId>Featbit-Java-SDK</artifactId>
48-
<version>1.0.4</version>
48+
<version>1.0.5</version>
4949
</dependency>
5050
</dependencies>
5151
```
@@ -184,16 +184,19 @@ if(client.isInitialized()){
184184
// Evaluation details
185185
FlagState<String> res = client.variationDetail("flag key", user, "Not Found");
186186
// Flag value
187-
String res = client.variation("flag key", user, "Not Found");
187+
String value = client.variation("flag key", user, "Not Found");
188+
188189
// get all variations for a given user
189-
AllFlagStates<String> res = client.getAllLatestFlagsVariations(user);
190+
AllFlagStates states = client.getAllLatestFlagsVariations(user);
191+
EvalDetail<String> detail = states.getStringDetail("flag key", user, "Not Found");
192+
value = states.getString("flag key", user, "Not Found");
190193
}
191194
```
192195

193196
If evaluation called before Java SDK client initialized or you set the wrong flag key or user for the evaluation, SDK will return
194197
the default value you set. The `FlagState` and `AllFlagStates` will all details of later evaluation including the error reason.
195198

196-
SDK supports String, Boolean, and Number as the return type of flag values, see JavaDocs for more details.
199+
SDK supports String, Boolean, and Number and Json as the return type of flag values, see JavaDocs for more details.
197200

198201
### Experiments (A/B/n Testing)
199202
We support automatic experiments for pageviews and clicks, you just need to set your experiment on our SaaS platform, then you should be able to see the result in near real time after the experiment is started.

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>co.featbit</groupId>
88
<artifactId>featbit-java-sdk</artifactId>
9-
<version>1.0.4</version>
9+
<version>1.0.5</version>
1010

1111
<name>featbit/featbit-java-sdk</name>
1212

src/main/java/co/featbit/commons/model/AllFlagStates.java

+201-48
Original file line numberDiff line numberDiff line change
@@ -1,120 +1,273 @@
11
package co.featbit.commons.model;
22

33
import co.featbit.commons.json.JsonHelper;
4+
import co.featbit.commons.json.JsonParseException;
45
import com.google.common.base.MoreObjects;
56
import com.google.common.collect.ImmutableList;
67
import com.google.common.collect.ImmutableMap;
78
import com.google.common.reflect.TypeToken;
9+
import org.apache.commons.lang3.BooleanUtils;
10+
import org.apache.commons.lang3.StringUtils;
811

912
import java.io.Serializable;
1013
import java.util.List;
1114
import java.util.Map;
1215
import java.util.Objects;
13-
import java.util.function.Function;
1416

1517
/**
1618
* The object provides a standard return responding the request of getting all flag values from a client sdk
17-
*
18-
* @param <T> String/Boolean/Numeric Type
1919
*/
20-
public class AllFlagStates<T> extends BasicFlagState implements Serializable {
21-
private List<EvalDetail<T>> data;
20+
public class AllFlagStates extends BasicFlagState implements Serializable {
2221

23-
private transient Map<String, EvalDetail<T>> cache;
22+
protected static final String DEFAULT_JSON_VALUE = "DJV";
2423

24+
protected static final String FLAG_VALUE_PARSING_ERROR = "flag value parsing error";
2525

26-
protected AllFlagStates(boolean success, String message, List<EvalDetail<T>> data) {
26+
protected static final String FLAG_NAME_UNKNOWN = "flag name unknown";
27+
28+
private List<EvalDetail<String>> data;
29+
30+
protected transient Map<String, EvalDetail<String>> cache;
31+
32+
33+
protected AllFlagStates(boolean success, String message, List<EvalDetail<String>> data) {
2734
super(success, success ? "OK" : message);
2835
init(data);
2936
}
3037

31-
private void init(List<EvalDetail<T>> data) {
32-
ImmutableMap.Builder<String, EvalDetail<T>> builder = ImmutableMap.builder();
38+
private void init(List<EvalDetail<String>> data) {
39+
ImmutableMap.Builder<String, EvalDetail<String>> builder = ImmutableMap.builder();
3340
this.data = data == null ? ImmutableList.of() : ImmutableList.copyOf(data);
34-
for (EvalDetail<T> detail : data) {
41+
for (EvalDetail<String> detail : data) {
3542
builder.put(detail.getKeyName(), detail);
3643
}
3744
this.cache = builder.build();
3845
}
3946

40-
/**
41-
* build a AllFlagStates without flag value
42-
*
43-
* @param message the reason without flag value
44-
* @return a AllFlagStates
45-
*/
46-
public static <T> AllFlagStates<T> empty(String message) {
47-
return new AllFlagStates(false, message, null);
48-
}
49-
5047
/**
5148
* build a AllFlagStates
5249
*
5350
* @param success true if the last request is successful
5451
* @param message the reason
5552
* @param data all flag values
56-
* @param <T> String/Boolean/Numeric Type
5753
* @return a AllFlagStates
5854
*/
59-
public static <T> AllFlagStates<T> of(boolean success, String message, List<EvalDetail<T>> data) {
55+
public static AllFlagStates of(boolean success, String message, List<EvalDetail<String>> data) {
6056
return new AllFlagStates(success, success ? "OK" : message, data);
6157
}
6258

6359
/**
6460
* build a AllFlagStates from json
6561
*
6662
* @param json a string json
67-
* @param cls
68-
* @param <T> String/Boolean/Numeric Type
6963
* @return a AllFlagStates
7064
*/
71-
public static <T> AllFlagStates<T> fromJson(String json, Class<T> cls) {
72-
return JsonHelper.deserialize(json, new TypeToken<AllFlagStates<T>>() {
65+
public static AllFlagStates fromJson(String json) {
66+
return JsonHelper.deserialize(json, new TypeToken<AllFlagStates>() {
7367
}.getType());
7468
}
7569

70+
private <T> EvalDetail<T> getInternal(String flagKeyName, T defaultValue, Class<T> clzz) {
71+
if (cache == null || cache.isEmpty()) {
72+
init(data);
73+
}
74+
EvalDetail<String> ed = cache.get(flagKeyName);
75+
if (ed == null) return EvalDetail.of(defaultValue, FLAG_VALUE_PARSING_ERROR, flagKeyName, FLAG_NAME_UNKNOWN);
76+
Object res = null;
77+
String variation = ed.getVariation();
78+
if (clzz == String.class) {
79+
res = variation;
80+
} else if (clzz == Boolean.class) {
81+
res = BooleanUtils.toBooleanObject(variation);
82+
} else if (clzz == Integer.class || clzz == Double.class || clzz == Long.class) {
83+
if (StringUtils.isNumeric(variation)) {
84+
if (clzz == Integer.class) {
85+
res = Integer.parseInt(variation);
86+
} else if (clzz == Double.class) {
87+
res = Double.parseDouble(variation);
88+
} else {
89+
res = Long.parseLong(variation);
90+
}
91+
}
92+
}
93+
return res == null ? EvalDetail.of(defaultValue, FLAG_VALUE_PARSING_ERROR, flagKeyName, ed.getName()) : EvalDetail.of(clzz.cast(res), ed.getReason(), flagKeyName, ed.getName());
94+
}
95+
96+
/**
97+
* return the string value of a given flag key name or default value if flag not existed
98+
*
99+
* @param flagKeyName the unique key for the feature flag
100+
* @param defaultValue the default value of the flag
101+
* @return string value or default value if flag not existed
102+
*/
103+
public String getString(String flagKeyName, String defaultValue) {
104+
return getInternal(flagKeyName, defaultValue, String.class).getVariation();
105+
}
106+
107+
/**
108+
* returns an object that describes the way the string flag value was determined.
109+
* Note that default value in the detail if flag not existed
110+
*
111+
* @param flagKeyName the unique key for the feature flag
112+
* @param defaultValue the default value of the flag
113+
* @return an {@link EvalDetail} object
114+
*/
115+
public EvalDetail<String> getStringDetail(String flagKeyName, String defaultValue) {
116+
return getInternal(flagKeyName, defaultValue, String.class);
117+
}
118+
119+
/**
120+
* return the Boolean value of a given flag key name, default value if flag not existed
121+
* or this value is not a Boolean
122+
*
123+
* @param flagKeyName the unique key for the feature flag
124+
* @param defaultValue the default value of the flag
125+
* @return the Boolean value, default value if flag not existed or this value is not a Boolean
126+
*/
127+
public Boolean getBoolean(String flagKeyName, Boolean defaultValue) {
128+
return getInternal(flagKeyName, defaultValue, Boolean.class).getVariation();
129+
}
130+
131+
/**
132+
* returns an object that describes the way the Boolean flag value was determined.
133+
* Note that default value in the detail if flag not existed or the value is not a Boolean
134+
*
135+
* @param flagKeyName the unique key for the feature flag
136+
* @param defaultValue the default value of the flag
137+
* @return an {@link EvalDetail} object
138+
*/
139+
public EvalDetail<Boolean> getBooleanDetail(String flagKeyName, Boolean defaultValue) {
140+
return getInternal(flagKeyName, defaultValue, Boolean.class);
141+
}
142+
143+
/**
144+
* return the Integer value of a given flag key name, default value if flag not existed
145+
* or this value is not an Integer
146+
*
147+
* @param flagKeyName the unique key for the feature flag
148+
* @param defaultValue the default value of the flag
149+
* @return the Integer value, default value if flag not existed or this value is not an Integer
150+
*/
151+
public Integer getInteger(String flagKeyName, Integer defaultValue) {
152+
return getInternal(flagKeyName, defaultValue, Integer.class).getVariation();
153+
}
154+
155+
/**
156+
* returns an object that describes the way the Integer flag value was determined.
157+
* Note that default value in the detail if flag not existed or the value is not an Integer
158+
*
159+
* @param flagKeyName the unique key for the feature flag
160+
* @param defaultValue the default value of the flag
161+
* @return an {@link EvalDetail} object
162+
*/
163+
public EvalDetail<Integer> getIntegerDetail(String flagKeyName, Integer defaultValue) {
164+
return getInternal(flagKeyName, defaultValue, Integer.class);
165+
}
166+
76167
/**
77-
* return details of all the flags
168+
* return the Long value of a given flag key name, default value if flag not existed
169+
* or this value is not a Long
78170
*
79-
* @return a map of flag key name and the {@link Function} to get the its {@link EvalDetail}
171+
* @param flagKeyName the unique key for the feature flag
172+
* @param defaultValue the default value of the flag
173+
* @return the Long value, default value if flag not existed or this value is not a Long
80174
*/
81-
public final Map<String, Function<String, EvalDetail<T>>> getData() {
82-
ImmutableMap.Builder<String, Function<String, EvalDetail<T>>> map = ImmutableMap.builder();
83-
for (EvalDetail<T> detail : data) {
84-
map.put(detail.getKeyName(), this::get);
175+
public Long getLong(String flagKeyName, Long defaultValue) {
176+
return getInternal(flagKeyName, defaultValue, Long.class).getVariation();
177+
}
178+
179+
/**
180+
* returns an object that describes the way the Long flag value was determined.
181+
* Note that default value in the detail if flag not existed or the value is not a Long
182+
*
183+
* @param flagKeyName the unique key for the feature flag
184+
* @param defaultValue the default value of the flag
185+
* @return an {@link EvalDetail} object
186+
*/
187+
public EvalDetail<Long> getLongDetail(String flagKeyName, Long defaultValue) {
188+
return getInternal(flagKeyName, defaultValue, Long.class);
189+
}
190+
191+
/**
192+
* return the Double value of a given flag key name, default value if flag not existed
193+
* or this value is not an Double
194+
*
195+
* @param flagKeyName the unique key for the feature flag
196+
* @param defaultValue the default value of the flag
197+
* @return the Double value, default value if flag not existe or this value is not an Double
198+
*/
199+
public Double getDouble(String flagKeyName, Double defaultValue) {
200+
return getInternal(flagKeyName, defaultValue, Double.class).getVariation();
201+
}
202+
203+
/**
204+
* returns an object that describes the way the Double flag value was determined.
205+
* Note that default value in the detail if flag not existed or the value is not a Double
206+
*
207+
* @param flagKeyName the unique key for the feature flag
208+
* @param defaultValue the default value of the flag
209+
* @return an {@link EvalDetail} object
210+
*/
211+
public EvalDetail<Double> getDoubleDetail(String flagKeyName, Double defaultValue) {
212+
return getInternal(flagKeyName, defaultValue, Double.class);
213+
}
214+
215+
/**
216+
* return the T type value of a given flag key name, default value if flag not existed
217+
* or this value is not a T type
218+
*
219+
* @param <T> the type of flag value
220+
* @param flagKeyName the unique key for the feature flag
221+
* @param defaultValue the default value of the flag
222+
* @return the Json value, default value if flag not existe or this value is not a T type
223+
*/
224+
public <T> T getJsonObject(String flagKeyName, T defaultValue, Class<T> clazz) {
225+
EvalDetail<String> ed = getInternal(flagKeyName, DEFAULT_JSON_VALUE, String.class);
226+
String json = ed.getVariation();
227+
if (DEFAULT_JSON_VALUE.equals(json)) {
228+
return defaultValue;
229+
}
230+
try {
231+
return JsonHelper.deserialize(json, clazz);
232+
} catch (JsonParseException e) {
233+
return defaultValue;
85234
}
86-
return map.build();
87235
}
88236

89237
/**
90-
* return a detail of a given flag key name
238+
* returns an object that describes the way the T type flag value was determined.
239+
* Note that default value in the detail if flag not existed or the value is not a T type
91240
*
92-
* @param flagKeyName flag key name
93-
* @return an {@link EvalDetail}
241+
* @param <T> the type of flag value
242+
* @param flagKeyName the unique key for the feature flag
243+
* @param defaultValue the default value of the flag
244+
* @return an {@link EvalDetail} object
94245
*/
95-
public EvalDetail<T> get(String flagKeyName) {
96-
if (cache == null || cache.isEmpty()) {
97-
init(data);
246+
public <T> EvalDetail<T> getJsonDetail(String flagKeyName, T defaultValue, Class<T> clazz) {
247+
EvalDetail<String> ed = getInternal(flagKeyName, DEFAULT_JSON_VALUE, String.class);
248+
String json = ed.getVariation();
249+
if (DEFAULT_JSON_VALUE.equals(json)) {
250+
return EvalDetail.of(defaultValue, ed.getReason(), flagKeyName, ed.getName());
251+
}
252+
try {
253+
T jsonValue = JsonHelper.deserialize(json, clazz);
254+
return EvalDetail.of(jsonValue, ed.getReason(), flagKeyName, ed.getName());
255+
} catch (JsonParseException e) {
256+
return EvalDetail.of(defaultValue, ed.getReason(), flagKeyName, ed.getName());
98257
}
99-
return cache.get(flagKeyName);
100258
}
101259

102260
@Override
103261
public String toString() {
104-
return MoreObjects.toStringHelper(this)
105-
.add("success", success)
106-
.add("message", message)
107-
.add("data", data)
108-
.toString();
262+
return MoreObjects.toStringHelper(this).add("success", success).add("message", message).add("data", data).toString();
109263
}
110264

111265
@Override
112266
public boolean equals(Object o) {
113267
if (this == o) return true;
114268
if (o == null || getClass() != o.getClass()) return false;
115-
AllFlagStates<?> that = (AllFlagStates<?>) o;
116-
return Objects.equals(data, that.data) && Objects.equals(message, that.message)
117-
&& Objects.equals(success, that.success);
269+
AllFlagStates that = (AllFlagStates) o;
270+
return Objects.equals(data, that.data) && Objects.equals(cache, that.cache);
118271
}
119272

120273
@Override

0 commit comments

Comments
 (0)