1515 * A {@code LDUser} object contains specific attributes of a user browsing your site. The only mandatory property property is the {@code key},
1616 * which must uniquely identify each user. For authenticated users, this may be a username or e-mail address. For anonymous users,
1717 * this could be an IP address or session ID.
18- *
18+ * <p>
1919 * Besides the mandatory {@code key}, {@code LDUser} supports two kinds of optional attributes: interpreted attributes (e.g. {@code ip} and {@code country})
2020 * and custom attributes. LaunchDarkly can parse interpreted attributes and attach meaning to them. For example, from an {@code ip} address, LaunchDarkly can
2121 * do a geo IP lookup and determine the user's country.
22- *
22+ * <p>
2323 * Custom attributes are not parsed by LaunchDarkly. They can be used in custom rules-- for example, a custom attribute such as "customer_ranking" can be used to
2424 * launch a feature to the top 10% of users on a site.
2525 */
@@ -33,7 +33,6 @@ public class LDUser {
3333 private JsonPrimitive firstName ;
3434 private JsonPrimitive lastName ;
3535 private JsonPrimitive anonymous ;
36-
3736 private JsonPrimitive country ;
3837 private Map <String , JsonElement > custom ;
3938 private static final Logger logger = LoggerFactory .getLogger (LDUser .class );
@@ -44,21 +43,22 @@ public class LDUser {
4443 }
4544
4645 protected LDUser (Builder builder ) {
47- this .key = builder .key == null ? null : new JsonPrimitive (builder .key );
48- this .ip = builder .ip == null ? null : new JsonPrimitive (builder .ip );
49- this .country = builder .country == null ? null : new JsonPrimitive (builder .country .getAlpha2 ());
46+ this .key = builder .key == null ? null : new JsonPrimitive (builder .key );
47+ this .ip = builder .ip == null ? null : new JsonPrimitive (builder .ip );
48+ this .country = builder .country == null ? null : new JsonPrimitive (builder .country .getAlpha2 ());
5049 this .secondary = builder .secondary == null ? null : new JsonPrimitive (builder .secondary );
5150 this .firstName = builder .firstName == null ? null : new JsonPrimitive (builder .firstName );
5251 this .lastName = builder .lastName == null ? null : new JsonPrimitive (builder .lastName );
5352 this .email = builder .email == null ? null : new JsonPrimitive (builder .email );
5453 this .name = builder .name == null ? null : new JsonPrimitive (builder .name );
5554 this .avatar = builder .avatar == null ? null : new JsonPrimitive (builder .avatar );
56- this .anonymous = builder .anonymous == null ? null : new JsonPrimitive (builder .anonymous );
55+ this .anonymous = builder .anonymous == null ? null : new JsonPrimitive (builder .anonymous );
5756 this .custom = new HashMap <>(builder .custom );
5857 }
5958
6059 /**
6160 * Create a user with the given key
61+ *
6262 * @param key a {@code String} that uniquely identifies a user
6363 */
6464 public LDUser (String key ) {
@@ -70,23 +70,41 @@ JsonPrimitive getKey() {
7070 return key ;
7171 }
7272
73- JsonPrimitive getIp () { return ip ; }
73+ JsonPrimitive getIp () {
74+ return ip ;
75+ }
7476
75- JsonPrimitive getCountry () { return country ; }
77+ JsonPrimitive getCountry () {
78+ return country ;
79+ }
7680
77- JsonPrimitive getSecondary () { return secondary ; }
81+ JsonPrimitive getSecondary () {
82+ return secondary ;
83+ }
7884
79- JsonPrimitive getName () { return name ; }
85+ JsonPrimitive getName () {
86+ return name ;
87+ }
8088
81- JsonPrimitive getFirstName () { return firstName ; }
89+ JsonPrimitive getFirstName () {
90+ return firstName ;
91+ }
8292
83- JsonPrimitive getLastName () { return lastName ; }
93+ JsonPrimitive getLastName () {
94+ return lastName ;
95+ }
8496
85- JsonPrimitive getEmail () { return email ; }
97+ JsonPrimitive getEmail () {
98+ return email ;
99+ }
86100
87- JsonPrimitive getAvatar () { return avatar ; }
101+ JsonPrimitive getAvatar () {
102+ return avatar ;
103+ }
88104
89- JsonPrimitive getAnonymous () { return anonymous ; }
105+ JsonPrimitive getAnonymous () {
106+ return anonymous ;
107+ }
90108
91109 JsonElement getCustom (String key ) {
92110 return custom .get (key );
@@ -95,14 +113,13 @@ JsonElement getCustom(String key) {
95113 /**
96114 * A <a href="http://en.wikipedia.org/wiki/Builder_pattern">builder</a> that helps construct {@link com.launchdarkly.client.LDUser} objects. Builder
97115 * calls can be chained, enabling the following pattern:
98- *
116+ * <p>
99117 * <pre>
100118 * LDUser user = new LDUser.Builder("key")
101119 * .country("US")
102120 * .ip("192.168.0.1")
103121 * .build()
104122 * </pre>
105- *
106123 */
107124 public static class Builder {
108125 private String key ;
@@ -119,6 +136,7 @@ public static class Builder {
119136
120137 /**
121138 * Create a builder with the specified key
139+ *
122140 * @param key the unique key for this user
123141 */
124142 public Builder (String key ) {
@@ -128,6 +146,7 @@ public Builder(String key) {
128146
129147 /**
130148 * Set the IP for a user
149+ *
131150 * @param s the IP address for the user
132151 * @return the builder
133152 */
@@ -145,6 +164,7 @@ public Builder secondary(String s) {
145164 * Set the country for a user. The country should be a valid <a href="http://en.wikipedia.org/wiki/ISO_3166-1">ISO 3166-1</a>
146165 * alpha-2 or alpha-3 code. If it is not a valid ISO-3166-1 code, an attempt will be made to look up the country by its name.
147166 * If that fails, a warning will be logged, and the country will not be set.
167+ *
148168 * @param s the country for the user
149169 * @return the builder
150170 */
@@ -156,8 +176,7 @@ public Builder country(String s) {
156176
157177 if (codes .isEmpty ()) {
158178 logger .warn ("Invalid country. Expected valid ISO-3166-1 code: " + s );
159- }
160- else if (codes .size () > 1 ) {
179+ } else if (codes .size () > 1 ) {
161180 // See if any of the codes is an exact match
162181 for (LDCountryCode c : codes ) {
163182 if (c .getName ().equals (s )) {
@@ -167,8 +186,7 @@ else if (codes.size() > 1) {
167186 }
168187 logger .warn ("Ambiguous country. Provided code matches multiple countries: " + s );
169188 country = codes .get (0 );
170- }
171- else {
189+ } else {
172190 country = codes .get (0 );
173191 }
174192
@@ -189,6 +207,7 @@ public Builder country(LDCountryCode country) {
189207
190208 /**
191209 * Sets the user's first name
210+ *
192211 * @param firstName the user's first name
193212 * @return the builder
194213 */
@@ -199,6 +218,7 @@ public Builder firstName(String firstName) {
199218
200219 /**
201220 * Sets whether this user is anonymous
221+ *
202222 * @param anonymous whether the user is anonymous
203223 * @return the builder
204224 */
@@ -209,6 +229,7 @@ public Builder anonymous(boolean anonymous) {
209229
210230 /**
211231 * Sets the user's last name
232+ *
212233 * @param lastName the user's last name
213234 * @return the builder
214235 */
@@ -219,6 +240,7 @@ public Builder lastName(String lastName) {
219240
220241 /**
221242 * Sets the user's full name
243+ *
222244 * @param name the user's full name
223245 * @return the builder
224246 */
@@ -229,6 +251,7 @@ public Builder name(String name) {
229251
230252 /**
231253 * Sets the user's avatar
254+ *
232255 * @param avatar the user's avatar
233256 * @return the builder
234257 */
@@ -239,6 +262,7 @@ public Builder avatar(String avatar) {
239262
240263 /**
241264 * Sets the user's e-mail address
265+ *
242266 * @param email the e-mail address
243267 * @return the builder
244268 */
@@ -248,62 +272,83 @@ public Builder email(String email) {
248272 }
249273
250274 /**
251- * Add a {@link java.lang.String}-valued custom attribute
252- * @param k the key for the custom attribute
275+ * Add a {@link java.lang.String}-valued custom attribute. When set to one of the
276+ * <a href="http://docs.launchdarkly.com/docs/targeting-users#targeting-based-on-user-attributes">
277+ * built-in user attribute keys</a>, this custom attribute will be ignored.
278+ *
279+ * @param k the key for the custom attribute.
253280 * @param v the value for the custom attribute
254281 * @return the builder
282+ * @see
255283 */
256284 public Builder custom (String k , String v ) {
257- if (key != null && v != null ) {
285+ checkCustomAttribute (k );
286+ if (k != null && v != null ) {
258287 custom .put (k , new JsonPrimitive (v ));
259288 }
260289 return this ;
261290 }
262291
263292 /**
264- * Add a {@link java.lang.Number}-valued custom attribute
265- * @param k the key for the custom attribute
293+ * Add a {@link java.lang.Number}-valued custom attribute. When set to one of the
294+ * <a href="http://docs.launchdarkly.com/docs/targeting-users#targeting-based-on-user-attributes">
295+ * built-in user attribute keys</a>, this custom attribute will be ignored.
296+ *
297+ * @param k the key for the custom attribute. When set to one of the built-in user attribute keys, this custom attribute will be ignored.
266298 * @param n the value for the custom attribute
267299 * @return the builder
268300 */
269301 public Builder custom (String k , Number n ) {
270- if (key != null && n != null ) {
302+ checkCustomAttribute (k );
303+ if (k != null && n != null ) {
271304 custom .put (k , new JsonPrimitive (n ));
272305 }
273306 return this ;
274307 }
275308
276309 /**
277- * Add a {@link java.lang.Boolean}-valued custom attribute
278- * @param k the key for the custom attribute
310+ * Add a {@link java.lang.Boolean}-valued custom attribute. When set to one of the
311+ * <a href="http://docs.launchdarkly.com/docs/targeting-users#targeting-based-on-user-attributes">
312+ * built-in user attribute keys</a>, this custom attribute will be ignored.
313+ *
314+ * @param k the key for the custom attribute. When set to one of the built-in user attribute keys, this custom attribute will be ignored.
279315 * @param b the value for the custom attribute
280316 * @return the builder
281317 */
282318 public Builder custom (String k , Boolean b ) {
283- if (key != null && b != null ) {
319+ checkCustomAttribute (k );
320+ if (k != null && b != null ) {
284321 custom .put (k , new JsonPrimitive (b ));
285322 }
286323 return this ;
287324 }
288325
289326 /**
290- * Add a list of {@link java.lang.String}-valued custom attributes
291- * @param k the key for the list
327+ * Add a list of {@link java.lang.String}-valued custom attributes. When set to one of the
328+ * <a href="http://docs.launchdarkly.com/docs/targeting-users#targeting-based-on-user-attributes">
329+ * built-in user attribute keys</a>, this custom attribute will be ignored.
330+ *
331+ * @param k the key for the list. When set to one of the built-in user attribute keys, this custom attribute will be ignored.
292332 * @param vs the values for the attribute
293333 * @return the builder
294334 * @deprecated As of version 0.16.0, renamed to {@link #customString(String, List) customString}
295335 */
296336 public Builder custom (String k , List <String > vs ) {
337+ checkCustomAttribute (k );
297338 return this .customString (k , vs );
298339 }
299-
340+
300341 /**
301- * Add a list of {@link java.lang.String}-valued custom attributes
302- * @param k the key for the list
342+ * Add a list of {@link java.lang.String}-valued custom attributes. When set to one of the
343+ * <a href="http://docs.launchdarkly.com/docs/targeting-users#targeting-based-on-user-attributes">
344+ * built-in user attribute keys</a>, this custom attribute will be ignored.
345+ *
346+ * @param k the key for the list. When set to one of the built-in user attribute keys, this custom attribute will be ignored.
303347 * @param vs the values for the attribute
304348 * @return the builder
305349 */
306350 public Builder customString (String k , List <String > vs ) {
351+ checkCustomAttribute (k );
307352 JsonArray array = new JsonArray ();
308353 for (String v : vs ) {
309354 if (v != null ) {
@@ -313,14 +358,18 @@ public Builder customString(String k, List<String> vs) {
313358 custom .put (k , array );
314359 return this ;
315360 }
316-
361+
317362 /**
318- * Add a list of {@link java.lang.Integer}-valued custom attributes
319- * @param k the key for the list
363+ * Add a list of {@link java.lang.Integer}-valued custom attributes. When set to one of the
364+ * <a href="http://docs.launchdarkly.com/docs/targeting-users#targeting-based-on-user-attributes">
365+ * built-in user attribute keys</a>, this custom attribute will be ignored.
366+ *
367+ * @param k the key for the list. When set to one of the built-in user attribute keys, this custom attribute will be ignored.
320368 * @param vs the values for the attribute
321369 * @return the builder
322370 */
323371 public Builder customNumber (String k , List <Number > vs ) {
372+ checkCustomAttribute (k );
324373 JsonArray array = new JsonArray ();
325374 for (Number v : vs ) {
326375 if (v != null ) {
@@ -331,8 +380,18 @@ public Builder customNumber(String k, List<Number> vs) {
331380 return this ;
332381 }
333382
383+ private void checkCustomAttribute (String key ) {
384+ for (UserAttribute a : UserAttribute .values ()) {
385+ if (a .name ().equals (key )) {
386+ logger .warn ("Built-in attribute key: " + key + " added as custom attribute! This custom attribute will be ignored during Feature Flag evaluation" );
387+ return ;
388+ }
389+ }
390+ }
391+
334392 /**
335393 * Build the configured {@link com.launchdarkly.client.LDUser} object
394+ *
336395 * @return the {@link com.launchdarkly.client.LDUser} configured by this builder
337396 */
338397 public LDUser build () {
0 commit comments