Skip to content

Commit a0b7641

Browse files
Use default flags in local evaluation mode (#107)
* Remove stale imports * Swallow errors updating environment and use default flags * Add / update tests * Add logic to handle missing default handler * formatting * checkstyle / test fixes * Tidy up logging and add test * formatting * remove duplicate dep
1 parent 9ca05cf commit a0b7641

File tree

4 files changed

+741
-627
lines changed

4 files changed

+741
-627
lines changed

pom.xml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,6 @@
9797
</dependency>
9898

9999
<!-- Test Dependencies-->
100-
<dependency>
101-
<groupId>org.testng</groupId>
102-
<artifactId>testng</artifactId>
103-
<version>7.6.1</version>
104-
<scope>test</scope>
105-
</dependency>
106100
<dependency>
107101
<groupId>org.slf4j</groupId>
108102
<artifactId>slf4j-simple</artifactId>

src/main/java/com/flagsmith/FlagsmithClient.java

Lines changed: 96 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.flagsmith;
22

3-
import com.fasterxml.jackson.databind.JsonNode;
43
import com.flagsmith.config.FlagsmithCacheConfig;
54
import com.flagsmith.config.FlagsmithConfig;
65
import com.flagsmith.exceptions.FlagsmithApiError;
@@ -17,20 +16,16 @@
1716
import com.flagsmith.models.BaseFlag;
1817
import com.flagsmith.models.Flags;
1918
import com.flagsmith.models.Segment;
20-
import com.flagsmith.threads.AnalyticsProcessor;
2119
import com.flagsmith.threads.PollingManager;
22-
import java.util.ArrayList;
23-
import java.util.Arrays;
2420
import java.util.HashMap;
2521
import java.util.List;
2622
import java.util.Map;
27-
import java.util.Set;
2823
import java.util.function.Function;
29-
import java.util.function.Predicate;
3024
import java.util.stream.Collectors;
3125

3226
import lombok.Data;
3327
import lombok.NonNull;
28+
import org.slf4j.Logger;
3429
import org.slf4j.LoggerFactory;
3530

3631
/**
@@ -43,10 +38,9 @@ public class FlagsmithClient {
4338
private FlagsmithSdk flagsmithSdk;
4439
private EnvironmentModel environment;
4540
private PollingManager pollingManager;
46-
private static final String UNABLE_TO_UPDATE_ENVIRONMENT_MESSAGE =
47-
"Unable to update environment from API. Continuing to use previous copy.";
4841

49-
private FlagsmithClient() { }
42+
private FlagsmithClient() {
43+
}
5044

5145
public static FlagsmithClient.Builder newBuilder() {
5246
return new FlagsmithClient.Builder();
@@ -64,20 +58,10 @@ public void updateEnvironment() {
6458
if (updatedEnvironment != null) {
6559
this.environment = updatedEnvironment;
6660
} else {
67-
logger.error(UNABLE_TO_UPDATE_ENVIRONMENT_MESSAGE);
61+
logger.error(getEnvironmentUpdateErrorMessage());
6862
}
6963
} catch (RuntimeException e) {
70-
// if we already have a copy of the environment, don't throw an error,
71-
// just continue using that one and log an error as it's likely just a
72-
// temporary network issue.
73-
if (this.environment != null) {
74-
logger.error(UNABLE_TO_UPDATE_ENVIRONMENT_MESSAGE);
75-
} else {
76-
// if we don't already have an environment, then we should still throw
77-
// the error since it's likely on client start up and there might be
78-
// something more sinister going on.
79-
throw e;
80-
}
64+
logger.error(getEnvironmentUpdateErrorMessage());
8165
}
8266
}
8367

@@ -86,8 +70,8 @@ public void updateEnvironment() {
8670
*
8771
* @return
8872
*/
89-
public Flags getEnvironmentFlags() throws FlagsmithApiError {
90-
if (environment != null) {
73+
public Flags getEnvironmentFlags() throws FlagsmithClientError {
74+
if (flagsmithSdk.getConfig().getEnableLocalEvaluation()) {
9175
return getEnvironmentFlagsFromDocument();
9276
}
9377

@@ -97,7 +81,8 @@ public Flags getEnvironmentFlags() throws FlagsmithApiError {
9781
/**
9882
* Get all the flags for the current environment for a given identity. Will also
9983
* upsert all traits to the Flagsmith API for future evaluations. Providing a
100-
* trait with a value of None will remove the trait from the identity if it exists.
84+
* trait with a value of None will remove the trait from the identity if it
85+
* exists.
10186
*
10287
* @param identifier identifier string
10388
* @return
@@ -110,15 +95,16 @@ public Flags getIdentityFlags(String identifier)
11095
/**
11196
* Get all the flags for the current environment for a given identity. Will also
11297
* upsert all traits to the Flagsmith API for future evaluations. Providing a
113-
* trait with a value of None will remove the trait from the identity if it exists.
98+
* trait with a value of None will remove the trait from the identity if it
99+
* exists.
114100
*
115101
* @param identifier identifier string
116-
* @param traits list of key value traits
102+
* @param traits list of key value traits
117103
* @return
118104
*/
119105
public Flags getIdentityFlags(String identifier, Map<String, Object> traits)
120106
throws FlagsmithClientError {
121-
if (environment != null) {
107+
if (flagsmithSdk.getConfig().getEnableLocalEvaluation()) {
122108
return getIdentityFlagsFromDocument(identifier, traits);
123109
}
124110

@@ -129,7 +115,7 @@ public Flags getIdentityFlags(String identifier, Map<String, Object> traits)
129115
* Get a list of segments that the given identity is in.
130116
*
131117
* @param identifier a unique identifier for the identity in the current
132-
* environment, e.g. email address, username, uuid
118+
* environment, e.g. email address, username, uuid
133119
* @return
134120
*/
135121
public List<Segment> getIdentitySegments(String identifier)
@@ -141,9 +127,9 @@ public List<Segment> getIdentitySegments(String identifier)
141127
* Get a list of segments that the given identity is in.
142128
*
143129
* @param identifier a unique identifier for the identity in the current
144-
* environment, e.g. email address, username, uuid
145-
* @param traits a dictionary of traits to add / update on the identity in
146-
* Flagsmith, e.g. {"num_orders": 10}
130+
* environment, e.g. email address, username, uuid
131+
* @param traits a dictionary of traits to add / update on the identity in
132+
* Flagsmith, e.g. {"num_orders": 10}
147133
* @return
148134
*/
149135
public List<Segment> getIdentitySegments(String identifier, Map<String, Object> traits)
@@ -152,11 +138,9 @@ public List<Segment> getIdentitySegments(String identifier, Map<String, Object>
152138
throw new FlagsmithClientError("Local evaluation required to obtain identity segments.");
153139
}
154140
IdentityModel identityModel = buildIdentityModel(
155-
identifier, (traits != null ? traits : new HashMap<>())
156-
);
141+
identifier, (traits != null ? traits : new HashMap<>()));
157142
List<SegmentModel> segmentModels = SegmentEvaluator.getIdentitySegments(
158-
environment, identityModel
159-
);
143+
environment, identityModel);
160144

161145
return segmentModels.stream().map((segmentModel) -> {
162146
Segment segment = new Segment();
@@ -168,46 +152,56 @@ public List<Segment> getIdentitySegments(String identifier, Map<String, Object>
168152
}
169153

170154
/**
171-
* Should be called when terminating the client to clean up any resources that need cleaning up.
172-
**/
155+
* Should be called when terminating the client to clean up any resources that
156+
* need cleaning up.
157+
**/
173158
public void close() {
174159
if (pollingManager != null) {
175160
pollingManager.stopPolling();
176161
}
177162
flagsmithSdk.close();
178163
}
179164

180-
private Flags getEnvironmentFlagsFromDocument() {
165+
private Flags getEnvironmentFlagsFromDocument() throws FlagsmithClientError {
166+
if (environment == null) {
167+
if (flagsmithSdk.getConfig().getFlagsmithFlagDefaults() == null) {
168+
throw new FlagsmithClientError("Unable to get flags. No environment present.");
169+
}
170+
return getDefaultFlags();
171+
}
172+
181173
return Flags.fromFeatureStateModels(
182174
Engine.getEnvironmentFeatureStates(environment),
183175
flagsmithSdk.getConfig().getAnalyticsProcessor(),
184176
null,
185-
flagsmithSdk.getConfig().getFlagsmithFlagDefaults()
186-
);
177+
flagsmithSdk.getConfig().getFlagsmithFlagDefaults());
187178
}
188179

189180
private Flags getIdentityFlagsFromDocument(String identifier, Map<String, Object> traits)
190181
throws FlagsmithClientError {
182+
if (environment == null) {
183+
if (flagsmithSdk.getConfig().getFlagsmithFlagDefaults() == null) {
184+
throw new FlagsmithClientError("Unable to get flags. No environment present.");
185+
}
186+
return getDefaultFlags();
187+
}
188+
191189
IdentityModel identity = buildIdentityModel(identifier, traits);
192190
List<FeatureStateModel> featureStates = Engine.getIdentityFeatureStates(environment, identity);
193191

194192
return Flags.fromFeatureStateModels(
195193
featureStates,
196194
flagsmithSdk.getConfig().getAnalyticsProcessor(),
197195
identity.getCompositeKey(),
198-
flagsmithSdk.getConfig().getFlagsmithFlagDefaults()
199-
);
196+
flagsmithSdk.getConfig().getFlagsmithFlagDefaults());
200197
}
201198

202199
private Flags getEnvironmentFlagsFromApi() throws FlagsmithApiError {
203200
try {
204201
return flagsmithSdk.getFeatureFlags(Boolean.TRUE);
205202
} catch (Exception e) {
206203
if (flagsmithSdk.getConfig().getFlagsmithFlagDefaults() != null) {
207-
Flags flags = new Flags();
208-
flags.setDefaultFlagHandler(flagsmithSdk.getConfig().getFlagsmithFlagDefaults());
209-
210-
return flags;
204+
return getDefaultFlags();
211205
}
212206

213207
throw new FlagsmithApiError("Failed to get feature flags.");
@@ -228,14 +222,10 @@ private Flags getIdentityFlagsFromApi(String identifier, Map<String, Object> tra
228222
return flagsmithSdk.identifyUserWithTraits(
229223
identifier,
230224
traitsList,
231-
Boolean.TRUE
232-
);
225+
Boolean.TRUE);
233226
} catch (Exception e) {
234227
if (flagsmithSdk.getConfig().getFlagsmithFlagDefaults() != null) {
235-
Flags flags = new Flags();
236-
flags.setDefaultFlagHandler(flagsmithSdk.getConfig().getFlagsmithFlagDefaults());
237-
238-
return flags;
228+
return getDefaultFlags();
239229
}
240230

241231
throw new FlagsmithApiError("Failed to get feature flags.");
@@ -245,8 +235,8 @@ private Flags getIdentityFlagsFromApi(String identifier, Map<String, Object> tra
245235
private IdentityModel buildIdentityModel(String identifier, Map<String, Object> traits)
246236
throws FlagsmithClientError {
247237
if (environment == null) {
248-
throw new
249-
FlagsmithClientError("Unable to build identity model when no local environment present.");
238+
throw new FlagsmithClientError(
239+
"Unable to build identity model when no local environment present.");
250240
}
251241

252242
List<TraitModel> traitsList = traits.entrySet().stream().map((entry) -> {
@@ -265,8 +255,24 @@ private IdentityModel buildIdentityModel(String identifier, Map<String, Object>
265255
return identity;
266256
}
267257

258+
private Flags getDefaultFlags() {
259+
Flags flags = new Flags();
260+
flags.setDefaultFlagHandler(flagsmithSdk.getConfig().getFlagsmithFlagDefaults());
261+
return flags;
262+
}
263+
264+
private String getEnvironmentUpdateErrorMessage() {
265+
if (this.environment == null) {
266+
return "Unable to update environment from API. "
267+
+ "No environment configured - using defaultHandler if configured.";
268+
} else {
269+
return "Unable to update environment from API. Continuing to use previous copy.";
270+
}
271+
}
272+
268273
/**
269-
* Returns a FlagsmithCache cache object that encapsulates methods to manipulate the cache.
274+
* Returns a FlagsmithCache cache object that encapsulates methods to manipulate
275+
* the cache.
270276
*
271277
* @return a FlagsmithCache if enabled, otherwise null.
272278
*/
@@ -304,13 +310,16 @@ public Builder setApiKey(String apiKey) {
304310
}
305311

306312
/**
307-
* When a flag does not exist in Flagsmith or there is an error, the SDK will return null by
313+
* When a flag does not exist in Flagsmith or there is an error, the SDK will
314+
* return null by
308315
* default.
309316
*
310-
* <p>If you would like to override this default behaviour, you can use this method. By default
317+
* <p>If you would like to override this default behaviour, you can use this
318+
* method. By default
311319
* it will return null for any flags that it does not recognise.
312320
*
313-
* @param defaultFlagValueFunction the new function to use as default flag values
321+
* @param defaultFlagValueFunction the new function to use as default flag
322+
* values
314323
* @return the Builder
315324
*/
316325
public Builder setDefaultFlagValueFunction(
@@ -324,7 +333,8 @@ public Builder setDefaultFlagValueFunction(
324333
}
325334

326335
/**
327-
* Enables logging, the project importing this module must include an implementation slf4j in
336+
* Enables logging, the project importing this module must include an
337+
* implementation slf4j in
328338
* their pom.
329339
*
330340
* @param level log error level.
@@ -336,7 +346,8 @@ public Builder enableLogging(FlagsmithLoggerLevel level) {
336346
}
337347

338348
/**
339-
* Enables logging, the project importing this module must include an implementation slf4j in
349+
* Enables logging, the project importing this module must include an
350+
* implementation slf4j in
340351
* their pom.
341352
*
342353
* @return the Builder
@@ -346,6 +357,18 @@ public Builder enableLogging() {
346357
return this;
347358
}
348359

360+
/**
361+
* Enables logging, the project importing this module must include an
362+
* implementation slf4j in
363+
* their pom.
364+
*
365+
* @return the Builder
366+
*/
367+
public Builder enableLogging(Logger logger) {
368+
this.client.logger.setLogger(logger);
369+
return this;
370+
}
371+
349372
/**
350373
* Override default FlagsmithConfig for Flagsmith API.
351374
*
@@ -388,7 +411,8 @@ public Builder withCustomHttpHeaders(HashMap<String, String> customHeaders) {
388411
/**
389412
* Enable in-memory caching for the Flagsmith API.
390413
*
391-
* <p>If no other cache configuration is set, the Caffeine defaults will be used, i.e. no limit
414+
* <p>If no other cache configuration is set, the Caffeine defaults will be used,
415+
* i.e. no limit
392416
*
393417
* @param cacheConfig an FlagsmithCacheConfig.
394418
* @return the Builder
@@ -432,19 +456,17 @@ public FlagsmithClient build() {
432456
flagsmithApiWrapper = this.flagsmithApiWrapper;
433457
} else if (cacheConfig != null) {
434458
flagsmithApiWrapper = new FlagsmithApiWrapper(
435-
cacheConfig.getCache(),
436-
this.configuration,
437-
this.customHeaders,
438-
client.logger,
439-
apiKey
440-
);
459+
cacheConfig.getCache(),
460+
this.configuration,
461+
this.customHeaders,
462+
client.logger,
463+
apiKey);
441464
} else {
442465
flagsmithApiWrapper = new FlagsmithApiWrapper(
443-
this.configuration,
444-
this.customHeaders,
445-
client.logger,
446-
apiKey
447-
);
466+
this.configuration,
467+
this.customHeaders,
468+
client.logger,
469+
apiKey);
448470
}
449471

450472
client.flagsmithSdk = flagsmithApiWrapper;
@@ -458,17 +480,15 @@ public FlagsmithClient build() {
458480
if (!apiKey.startsWith("ser.")) {
459481
throw new RuntimeException(
460482
"In order to use local evaluation, please generate a server key "
461-
+ "in the environment settings page."
462-
);
483+
+ "in the environment settings page.");
463484
}
464485

465486
if (this.pollingManager != null) {
466487
client.pollingManager = pollingManager;
467488
} else {
468489
client.pollingManager = new PollingManager(
469490
client,
470-
configuration.getEnvironmentRefreshIntervalSeconds()
471-
);
491+
configuration.getEnvironmentRefreshIntervalSeconds());
472492
}
473493

474494
client.pollingManager.startPolling();

0 commit comments

Comments
 (0)