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

Commit 3fd7d43

Browse files
author
Dan Richelson
committed
Allow blank user key when evaluating. Extract client interface. No longer return null when expected type does not match variation from evaluation.
1 parent 209c655 commit 3fd7d43

File tree

4 files changed

+123
-31
lines changed

4 files changed

+123
-31
lines changed

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

Lines changed: 29 additions & 28 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,6 +128,7 @@ 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;
@@ -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,6 +161,7 @@ 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;
@@ -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.");
@@ -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) {
312+
private JsonElement evaluate(String featureKey, LDUser user, JsonElement defaultValue, VariationType expectedType) {
318313
if (user == null || user.getKey() == null) {
319314
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,6 +385,7 @@ 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) {
389390
if (user == null || user.getKey() == null) {
390391
return null;
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+
}
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+
}

src/test/java/com/launchdarkly/client/LDClientTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public class LDClientTest extends EasyMockSupport {
2424
private PollingProcessor pollingProcessor;
2525
private EventProcessor eventProcessor;
2626
private Future initFuture;
27-
private LDClient client;
27+
private LDClientInterface client;
2828

2929
@Before
3030
public void before() {
@@ -346,7 +346,7 @@ public void testSecureModeHash() {
346346
LDConfig config = new LDConfig.Builder()
347347
.offline(true)
348348
.build();
349-
LDClient client = new LDClient("secret", config);
349+
LDClientInterface client = new LDClient("secret", config);
350350
LDUser user = new LDUser.Builder("Message").build();
351351
assertEquals("aa747c502a898200f9e4fa21bac68136f886a0e27aec70ba06daf2e2a5cb5597", client.secureModeHash(user));
352352
}
@@ -356,7 +356,7 @@ private void assertDefaultValueIsReturned() {
356356
assertEquals(true, result);
357357
}
358358

359-
private LDClient createMockClient(LDConfig config) {
359+
private LDClientInterface createMockClient(LDConfig config) {
360360
return new LDClient("SDK_KEY", config) {
361361
@Override
362362
protected FeatureRequestor createFeatureRequestor(String sdkKey, LDConfig config) {

0 commit comments

Comments
 (0)