Skip to content
This repository was archived by the owner on May 30, 2024. It is now read-only.

Commit f14ef7b

Browse files
authored
Merge pull request #66 from launchdarkly/dr/evenMoreV2
Final v2 changes.
2 parents a7b102a + ae12405 commit f14ef7b

File tree

10 files changed

+150
-58
lines changed

10 files changed

+150
-58
lines changed

src/main/java/com/launchdarkly/client/FeatureFlag.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,13 @@ static Map<String, FeatureFlag> fromJsonMap(String json) {
5353
}
5454

5555
EvalResult evaluate(LDUser user, FeatureStore featureStore) throws EvaluationException {
56-
if (user == null || user.getKeyAsString().isEmpty()) {
57-
logger.warn("Null user or null/empty user key when evaluating flag: " + key + "; returning null");
58-
return null;
59-
}
6056
List<FeatureRequestEvent> prereqEvents = new ArrayList<>();
57+
58+
if (user == null || user.getKey() == null) {
59+
logger.warn("Null user or null user key when evaluating flag: " + key + "; returning null");
60+
return new EvalResult(null, prereqEvents);
61+
}
62+
6163
if (isOn()) {
6264
JsonElement value = evaluate(user, featureStore, prereqEvents);
6365
if (value != null) {

src/main/java/com/launchdarkly/client/FeatureRequestor.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ protected CloseableHttpClient createClient() {
5555
return client;
5656
}
5757

58-
Map<String, FeatureFlag> makeAllRequest() throws IOException {
58+
Map<String, FeatureFlag> getAllFlags() throws IOException {
5959
HttpCacheContext context = HttpCacheContext.create();
6060

6161
HttpGet request = config.getRequest(sdkKey, GET_LATEST_FLAGS_PATH);
@@ -102,13 +102,9 @@ void logCacheResponse(CacheResponseStatus status) {
102102
}
103103
}
104104

105-
FeatureFlag makeRequest(String featureKey, boolean latest) throws IOException {
105+
FeatureFlag getFlag(String featureKey) throws IOException {
106106
HttpCacheContext context = HttpCacheContext.create();
107-
108-
String resource = latest ? "/api/eval/latest-features/" : "/api/eval/features/";
109-
110-
HttpGet request = config.getRequest(sdkKey,resource + featureKey);
111-
107+
HttpGet request = config.getRequest(sdkKey, GET_LATEST_FLAGS_PATH + "/" + featureKey);
112108
CloseableHttpResponse response = null;
113109
try {
114110
response = client.execute(request, context);

src/main/java/com/launchdarkly/client/LDClient.java

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
import javax.crypto.Mac;
1313
import javax.crypto.spec.SecretKeySpec;
14-
import java.io.Closeable;
1514
import java.io.IOException;
1615
import java.io.UnsupportedEncodingException;
1716
import java.net.URL;
@@ -30,7 +29,7 @@
3029
* a single {@code LDClient} for the lifetime of their application.
3130
*/
3231
@ThreadSafe
33-
public class LDClient implements Closeable {
32+
public class LDClient implements LDClientInterface {
3433
private static final Logger logger = LoggerFactory.getLogger(LDClient.class);
3534
private static final String HMAC_ALGORITHM = "HmacSHA256";
3635
protected static final String CLIENT_VERSION = getClientVersion();
@@ -96,6 +95,7 @@ public LDClient(String sdkKey, LDConfig config) {
9695
}
9796
}
9897

98+
@Override
9999
public boolean initialized() {
100100
return isOffline() || config.useLdd || updateProcessor.initialized();
101101
}
@@ -128,12 +128,13 @@ protected PollingProcessor createPollingProcessor(LDConfig config) {
128128
* @param user the user that performed the event
129129
* @param data a JSON object containing additional data associated with the event
130130
*/
131+
@Override
131132
public void track(String eventName, LDUser user, JsonElement data) {
132133
if (isOffline()) {
133134
return;
134135
}
135-
if (user == null || user.getKeyAsString().isEmpty()) {
136-
logger.warn("Track called with empty/nil user or user key!");
136+
if (user == null || user.getKey() == null) {
137+
logger.warn("Track called with null user or null user key!");
137138
}
138139
boolean processed = eventProcessor.sendEvent(new CustomEvent(eventName, user, data));
139140
if (!processed) {
@@ -147,6 +148,7 @@ public void track(String eventName, LDUser user, JsonElement data) {
147148
* @param eventName the name of the event
148149
* @param user the user that performed the event
149150
*/
151+
@Override
150152
public void track(String eventName, LDUser user) {
151153
if (isOffline()) {
152154
return;
@@ -159,12 +161,13 @@ public void track(String eventName, LDUser user) {
159161
*
160162
* @param user the user to register
161163
*/
164+
@Override
162165
public void identify(LDUser user) {
163166
if (isOffline()) {
164167
return;
165168
}
166-
if (user == null || user.getKeyAsString().isEmpty()) {
167-
logger.warn("Identify called with empty/nil user or user key!");
169+
if (user == null || user.getKey() == null) {
170+
logger.warn("Identify called with null user or null user key!");
168171
}
169172
boolean processed = eventProcessor.sendEvent(new IdentifyEvent(user));
170173
if (!processed) {
@@ -194,6 +197,7 @@ private void sendFlagRequestEvent(String featureKey, LDUser user, JsonElement va
194197
* @param user the end user requesting the feature flags
195198
* @return a map from feature flag keys to {@code JsonElement} for the specified user
196199
*/
200+
@Override
197201
public Map<String, JsonElement> allFlags(LDUser user) {
198202
if (isOffline()) {
199203
logger.warn("allFlags() was called when client is in offline mode! Returning null.");
@@ -205,8 +209,8 @@ public Map<String, JsonElement> allFlags(LDUser user) {
205209
return null;
206210
}
207211

208-
if (user == null || user.getKeyAsString().isEmpty()) {
209-
logger.warn("allFlags() was called with null user or null/empty user key! returning null");
212+
if (user == null || user.getKey() == null) {
213+
logger.warn("allFlags() was called with null user or null user key! returning null");
210214
return null;
211215
}
212216

@@ -233,17 +237,16 @@ public Map<String, JsonElement> allFlags(LDUser user) {
233237
* @param defaultValue the default value of the flag
234238
* @return whether or not the flag should be enabled, or {@code defaultValue} if the flag is disabled in the LaunchDarkly control panel
235239
*/
240+
@Override
236241
public boolean boolVariation(String featureKey, LDUser user, boolean defaultValue) {
237-
JsonElement value = jsonVariation(featureKey, user, new JsonPrimitive(defaultValue));
238-
if (value.isJsonPrimitive() && value.getAsJsonPrimitive().isBoolean()) {
239-
return value.getAsJsonPrimitive().getAsBoolean();
240-
}
241-
return false;
242+
JsonElement value = evaluate(featureKey, user, new JsonPrimitive(defaultValue), VariationType.Boolean);
243+
return value.getAsJsonPrimitive().getAsBoolean();
242244
}
243245

244246
/**
245247
* @deprecated use {@link #boolVariation(String, LDUser, boolean)}
246248
*/
249+
@Override
247250
@Deprecated
248251
public boolean toggle(String featureKey, LDUser user, boolean defaultValue) {
249252
logger.warn("Deprecated method: Toggle() called. Use boolVariation() instead.");
@@ -258,12 +261,10 @@ public boolean toggle(String featureKey, LDUser user, boolean defaultValue) {
258261
* @param defaultValue the default value of the flag
259262
* @return the variation for the given user, or {@code defaultValue} if the flag is disabled in the LaunchDarkly control panel
260263
*/
264+
@Override
261265
public Integer intVariation(String featureKey, LDUser user, int defaultValue) {
262-
JsonElement value = jsonVariation(featureKey, user, new JsonPrimitive(defaultValue));
263-
if (value.isJsonPrimitive() && value.getAsJsonPrimitive().isNumber()) {
264-
return value.getAsJsonPrimitive().getAsInt();
265-
}
266-
return null;
266+
JsonElement value = evaluate(featureKey, user, new JsonPrimitive(defaultValue), VariationType.Integer);
267+
return value.getAsJsonPrimitive().getAsInt();
267268
}
268269

269270
/**
@@ -274,12 +275,10 @@ public Integer intVariation(String featureKey, LDUser user, int defaultValue) {
274275
* @param defaultValue the default value of the flag
275276
* @return the variation for the given user, or {@code defaultValue} if the flag is disabled in the LaunchDarkly control panel
276277
*/
278+
@Override
277279
public Double doubleVariation(String featureKey, LDUser user, Double defaultValue) {
278-
JsonElement value = jsonVariation(featureKey, user, new JsonPrimitive(defaultValue));
279-
if (value.isJsonPrimitive() && value.getAsJsonPrimitive().isNumber()) {
280-
return value.getAsJsonPrimitive().getAsDouble();
281-
}
282-
return null;
280+
JsonElement value = evaluate(featureKey, user, new JsonPrimitive(defaultValue), VariationType.Double);
281+
return value.getAsJsonPrimitive().getAsDouble();
283282
}
284283

285284
/**
@@ -290,12 +289,10 @@ public Double doubleVariation(String featureKey, LDUser user, Double defaultValu
290289
* @param defaultValue the default value of the flag
291290
* @return the variation for the given user, or {@code defaultValue} if the flag is disabled in the LaunchDarkly control panel
292291
*/
292+
@Override
293293
public String stringVariation(String featureKey, LDUser user, String defaultValue) {
294-
JsonElement value = jsonVariation(featureKey, user, new JsonPrimitive(defaultValue));
295-
if (value.isJsonPrimitive() && value.getAsJsonPrimitive().isString()) {
296-
return value.getAsJsonPrimitive().getAsString();
297-
}
298-
return null;
294+
JsonElement value = evaluate(featureKey, user, new JsonPrimitive(defaultValue), VariationType.String);
295+
return value.getAsJsonPrimitive().getAsString();
299296
}
300297

301298
/**
@@ -306,21 +303,21 @@ public String stringVariation(String featureKey, LDUser user, String defaultValu
306303
* @param defaultValue the default value of the flag
307304
* @return the variation for the given user, or {@code defaultValue} if the flag is disabled in the LaunchDarkly control panel
308305
*/
306+
@Override
309307
public JsonElement jsonVariation(String featureKey, LDUser user, JsonElement defaultValue) {
310-
if (isOffline()) {
311-
return defaultValue;
312-
}
313-
JsonElement value = evaluate(featureKey, user, defaultValue);
308+
JsonElement value = evaluate(featureKey, user, defaultValue, VariationType.Json);
314309
return value;
315310
}
316311

317-
private JsonElement evaluate(String featureKey, LDUser user, JsonElement defaultValue) {
318-
if (user == null || user.getKeyAsString().isEmpty()) {
319-
logger.warn("Null user or null/empty user key when evaluating flag: " + featureKey + "; returning default value");
312+
private JsonElement evaluate(String featureKey, LDUser user, JsonElement defaultValue, VariationType expectedType) {
313+
if (user == null || user.getKey() == null) {
314+
logger.warn("Null user or null user key when evaluating flag: " + featureKey + "; returning default value");
320315
sendFlagRequestEvent(featureKey, user, defaultValue, defaultValue, null);
321316
return defaultValue;
322317
}
323-
318+
if (user.getKeyAsString().isEmpty()) {
319+
logger.warn("User key is blank. Flag evaluation will proceed, but the user will not be stored in LaunchDarkly");
320+
}
324321
if (!initialized()) {
325322
logger.warn("Evaluation called before Client has been initialized for feature flag " + featureKey + "; returning default value");
326323
sendFlagRequestEvent(featureKey, user, defaultValue, defaultValue, null);
@@ -341,6 +338,7 @@ private JsonElement evaluate(String featureKey, LDUser user, JsonElement default
341338
}
342339
}
343340
if (evalResult.getValue() != null) {
341+
expectedType.assertResultType(evalResult.getValue());
344342
sendFlagRequestEvent(featureKey, user, evalResult.getValue(), defaultValue, featureFlag.getVersion());
345343
return evalResult.getValue();
346344
}
@@ -369,13 +367,15 @@ public void close() throws IOException {
369367
/**
370368
* Flushes all pending events
371369
*/
370+
@Override
372371
public void flush() {
373372
this.eventProcessor.flush();
374373
}
375374

376375
/**
377376
* @return whether the client is in offline mode
378377
*/
378+
@Override
379379
public boolean isOffline() {
380380
return config.offline;
381381
}
@@ -385,8 +385,9 @@ public boolean isOffline() {
385385
* @param user The User to be hashed along with the sdk key
386386
* @return the hash, or null if the hash could not be calculated.
387387
*/
388+
@Override
388389
public String secureModeHash(LDUser user) {
389-
if (user == null || user.getKeyAsString().isEmpty()) {
390+
if (user == null || user.getKey() == null) {
390391
return null;
391392
}
392393
try {
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.launchdarkly.client;
2+
3+
import com.google.gson.JsonElement;
4+
5+
import java.io.Closeable;
6+
import java.io.IOException;
7+
import java.util.Map;
8+
9+
public interface LDClientInterface extends Closeable {
10+
boolean initialized();
11+
12+
void track(String eventName, LDUser user, JsonElement data);
13+
14+
void track(String eventName, LDUser user);
15+
16+
void identify(LDUser user);
17+
18+
Map<String, JsonElement> allFlags(LDUser user);
19+
20+
boolean boolVariation(String featureKey, LDUser user, boolean defaultValue);
21+
22+
@Deprecated
23+
boolean toggle(String featureKey, LDUser user, boolean defaultValue);
24+
25+
Integer intVariation(String featureKey, LDUser user, int defaultValue);
26+
27+
Double doubleVariation(String featureKey, LDUser user, Double defaultValue);
28+
29+
String stringVariation(String featureKey, LDUser user, String defaultValue);
30+
31+
JsonElement jsonVariation(String featureKey, LDUser user, JsonElement defaultValue);
32+
33+
@Override
34+
void close() throws IOException;
35+
36+
void flush();
37+
38+
boolean isOffline();
39+
40+
String secureModeHash(LDUser user);
41+
}

src/main/java/com/launchdarkly/client/PollingProcessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public Future<Void> start() {
4848
@Override
4949
public void run() {
5050
try {
51-
store.init(requestor.makeAllRequest());
51+
store.init(requestor.getAllFlags());
5252
if (!initialized.getAndSet(true)) {
5353
logger.info("Initialized LaunchDarkly client.");
5454
initFuture.completed(null);

src/main/java/com/launchdarkly/client/RedisFeatureStore.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,10 @@ public Map<String, FeatureFlag> all() {
240240
Type type = new TypeToken<FeatureFlag>() {}.getType();
241241

242242
for (Map.Entry<String, String> entry : featuresJson.entrySet()) {
243-
FeatureFlag rep = gson.fromJson(entry.getValue(), type);
244-
result.put(entry.getKey(), rep);
243+
FeatureFlag featureFlag = gson.fromJson(entry.getValue(), type);
244+
if (!featureFlag.isDeleted()){
245+
result.put(entry.getKey(), featureFlag);
246+
}
245247
}
246248
return result;
247249
}

src/main/java/com/launchdarkly/client/StreamProcessor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public void onMessage(String name, MessageEvent event) throws Exception {
7676
}
7777
case INDIRECT_PUT:
7878
try {
79-
store.init(requestor.makeAllRequest());
79+
store.init(requestor.getAllFlags());
8080
if (!initialized.getAndSet(true)) {
8181
initFuture.completed(null);
8282
logger.info("Initialized LaunchDarkly client.");
@@ -88,7 +88,7 @@ public void onMessage(String name, MessageEvent event) throws Exception {
8888
case INDIRECT_PATCH:
8989
String key = event.getData();
9090
try {
91-
FeatureFlag feature = requestor.makeRequest(key, true);
91+
FeatureFlag feature = requestor.getFlag(key);
9292
store.upsert(key, feature);
9393
} catch (IOException e) {
9494
logger.error("Encountered exception in LaunchDarkly client", e);
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.launchdarkly.client;
2+
3+
4+
import com.google.gson.JsonElement;
5+
6+
public enum VariationType {
7+
Boolean {
8+
@Override
9+
void assertResultType(JsonElement result) throws EvaluationException {
10+
if (result.isJsonPrimitive() && result.getAsJsonPrimitive().isBoolean()) {
11+
return;
12+
}
13+
throw new EvaluationException("Feature flag evaluation expected result as boolean type, but got non-boolean type.");
14+
}
15+
},
16+
Integer {
17+
@Override
18+
void assertResultType(JsonElement result) throws EvaluationException {
19+
if (result.isJsonPrimitive() && result.getAsJsonPrimitive().isNumber()) {
20+
return;
21+
}
22+
throw new EvaluationException("Feature flag evaluation expected result as number type, but got non-number type.");
23+
}
24+
},
25+
Double {
26+
@Override
27+
void assertResultType(JsonElement result) throws EvaluationException {
28+
if (result.isJsonPrimitive() && result.getAsJsonPrimitive().isNumber()) {
29+
return;
30+
}
31+
throw new EvaluationException("Feature flag evaluation expected result as number type, but got non-number type.");
32+
}
33+
},
34+
String {
35+
@Override
36+
void assertResultType(JsonElement result) throws EvaluationException {
37+
if (result.isJsonPrimitive() && result.getAsJsonPrimitive().isString()) {
38+
return;
39+
}
40+
throw new EvaluationException("Feature flag evaluation expected result as string type, but got non-string type.");
41+
}
42+
},
43+
Json {
44+
@Override
45+
void assertResultType(JsonElement result) {
46+
}
47+
};
48+
49+
abstract void assertResultType(JsonElement result) throws EvaluationException;
50+
}

0 commit comments

Comments
 (0)