|
1 | 1 | package co.featbit.commons.model;
|
2 | 2 |
|
3 | 3 | import co.featbit.commons.json.JsonHelper;
|
| 4 | +import co.featbit.commons.json.JsonParseException; |
4 | 5 | import com.google.common.base.MoreObjects;
|
5 | 6 | import com.google.common.collect.ImmutableList;
|
6 | 7 | import com.google.common.collect.ImmutableMap;
|
7 | 8 | import com.google.common.reflect.TypeToken;
|
| 9 | +import org.apache.commons.lang3.BooleanUtils; |
| 10 | +import org.apache.commons.lang3.StringUtils; |
8 | 11 |
|
9 | 12 | import java.io.Serializable;
|
10 | 13 | import java.util.List;
|
11 | 14 | import java.util.Map;
|
12 | 15 | import java.util.Objects;
|
13 |
| -import java.util.function.Function; |
14 | 16 |
|
15 | 17 | /**
|
16 | 18 | * 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 |
19 | 19 | */
|
20 |
| -public class AllFlagStates<T> extends BasicFlagState implements Serializable { |
21 |
| - private List<EvalDetail<T>> data; |
| 20 | +public class AllFlagStates extends BasicFlagState implements Serializable { |
22 | 21 |
|
23 |
| - private transient Map<String, EvalDetail<T>> cache; |
| 22 | + protected static final String DEFAULT_JSON_VALUE = "DJV"; |
24 | 23 |
|
| 24 | + protected static final String FLAG_VALUE_PARSING_ERROR = "flag value parsing error"; |
25 | 25 |
|
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) { |
27 | 34 | super(success, success ? "OK" : message);
|
28 | 35 | init(data);
|
29 | 36 | }
|
30 | 37 |
|
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(); |
33 | 40 | this.data = data == null ? ImmutableList.of() : ImmutableList.copyOf(data);
|
34 |
| - for (EvalDetail<T> detail : data) { |
| 41 | + for (EvalDetail<String> detail : data) { |
35 | 42 | builder.put(detail.getKeyName(), detail);
|
36 | 43 | }
|
37 | 44 | this.cache = builder.build();
|
38 | 45 | }
|
39 | 46 |
|
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 |
| - |
50 | 47 | /**
|
51 | 48 | * build a AllFlagStates
|
52 | 49 | *
|
53 | 50 | * @param success true if the last request is successful
|
54 | 51 | * @param message the reason
|
55 | 52 | * @param data all flag values
|
56 |
| - * @param <T> String/Boolean/Numeric Type |
57 | 53 | * @return a AllFlagStates
|
58 | 54 | */
|
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) { |
60 | 56 | return new AllFlagStates(success, success ? "OK" : message, data);
|
61 | 57 | }
|
62 | 58 |
|
63 | 59 | /**
|
64 | 60 | * build a AllFlagStates from json
|
65 | 61 | *
|
66 | 62 | * @param json a string json
|
67 |
| - * @param cls |
68 |
| - * @param <T> String/Boolean/Numeric Type |
69 | 63 | * @return a AllFlagStates
|
70 | 64 | */
|
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>() { |
73 | 67 | }.getType());
|
74 | 68 | }
|
75 | 69 |
|
| 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 | + |
76 | 167 | /**
|
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 |
78 | 170 | *
|
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 |
80 | 174 | */
|
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; |
85 | 234 | }
|
86 |
| - return map.build(); |
87 | 235 | }
|
88 | 236 |
|
89 | 237 | /**
|
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 |
91 | 240 | *
|
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 |
94 | 245 | */
|
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()); |
98 | 257 | }
|
99 |
| - return cache.get(flagKeyName); |
100 | 258 | }
|
101 | 259 |
|
102 | 260 | @Override
|
103 | 261 | 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(); |
109 | 263 | }
|
110 | 264 |
|
111 | 265 | @Override
|
112 | 266 | public boolean equals(Object o) {
|
113 | 267 | if (this == o) return true;
|
114 | 268 | 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); |
118 | 271 | }
|
119 | 272 |
|
120 | 273 | @Override
|
|
0 commit comments