11package com .flagsmith ;
22
3- import com .fasterxml .jackson .databind .JsonNode ;
43import com .flagsmith .config .FlagsmithCacheConfig ;
54import com .flagsmith .config .FlagsmithConfig ;
65import com .flagsmith .exceptions .FlagsmithApiError ;
1716import com .flagsmith .models .BaseFlag ;
1817import com .flagsmith .models .Flags ;
1918import com .flagsmith .models .Segment ;
20- import com .flagsmith .threads .AnalyticsProcessor ;
2119import com .flagsmith .threads .PollingManager ;
22- import java .util .ArrayList ;
23- import java .util .Arrays ;
2420import java .util .HashMap ;
2521import java .util .List ;
2622import java .util .Map ;
27- import java .util .Set ;
2823import java .util .function .Function ;
29- import java .util .function .Predicate ;
3024import java .util .stream .Collectors ;
3125
3226import lombok .Data ;
3327import lombok .NonNull ;
28+ import org .slf4j .Logger ;
3429import 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