
com.configcat.User Maven / Gradle / Ivy
Show all versions of configcat-java-client Show documentation
package com.configcat;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* An object containing attributes to properly identify a given user for variation evaluation.
* Its only mandatory attribute is the {@code identifier}.
*
* Please note that the {@code User} class is not designed to be used as a DTO (data transfer object).
* (Since the type of the {@code attributes} property is polymorphic, it's not guaranteed that deserializing a serialized instance produces an instance with an identical or even valid data content.)
*/
public class User {
private static final String IDENTIFIER_KEY = "Identifier";
private static final String EMAIL = "Email";
private static final String COUNTRY = "Country";
private final String identifier;
private final Map attributes;
private User(String identifier, String email, String country, Map custom) {
this.identifier = identifier == null ? "" : identifier;
this.attributes = new TreeMap<>();
this.attributes.put(IDENTIFIER_KEY, identifier);
if (country != null && !country.isEmpty()) {
this.attributes.put(COUNTRY, country);
}
if (email != null && !email.isEmpty()) {
this.attributes.put(EMAIL, email);
}
if (custom != null) {
for (Map.Entry entry : custom.entrySet()) {
if (!entry.getKey().equals(IDENTIFIER_KEY) && !entry.getKey().equals(COUNTRY) && !entry.getKey().equals(EMAIL)) {
this.attributes.put(entry.getKey(), entry.getValue());
}
}
}
}
public String getIdentifier() {
return this.identifier;
}
/**
* Creates a new builder instance.
*
* @return the new builder.
*/
public static Builder newBuilder() {
return new Builder();
}
public Object getAttribute(String key) {
if (key == null)
throw new IllegalArgumentException("key is null or empty");
return this.attributes.getOrDefault(key, null);
}
@Override
public String toString() {
LinkedHashMap tmp = new LinkedHashMap<>();
if (attributes.containsKey(IDENTIFIER_KEY)) {
tmp.put(IDENTIFIER_KEY, attributes.get(IDENTIFIER_KEY));
}
if (attributes.containsKey(EMAIL)) {
tmp.put(EMAIL, attributes.get(EMAIL));
}
if (attributes.containsKey(COUNTRY)) {
tmp.put(COUNTRY, attributes.get(COUNTRY));
}
for (Map.Entry entry : attributes.entrySet()) {
if (!entry.getKey().equals(IDENTIFIER_KEY) && !entry.getKey().equals(COUNTRY) && !entry.getKey().equals(EMAIL)) {
tmp.put(entry.getKey(), entry.getValue());
}
}
return Utils.gson.toJson(tmp);
}
/**
* A builder that helps construct a {@link User} instance.
*/
public static class Builder {
private String email;
private String country;
private Map custom;
/**
* Optional. Sets the email of the user.
*
* @param email the email address.
* @return the builder.
*/
public Builder email(String email) {
this.email = email;
return this;
}
/**
* Optional. Sets the country of the user.
*
* @param country the country.
* @return the builder.
*/
public Builder country(String country) {
this.country = country;
return this;
}
/**
* Optional. Sets the custom attributes of a user
*
* Custom attributes of the user for advanced targeting rule definitions (e.g. user role, subscription type, etc.)
*
* The set of allowed attribute values depends on the comparison type of the condition which references the User Object attribute.
* {@link String} values are supported by all comparison types (in some cases they need to be provided in a specific format though).
* Some of the comparison types work with other types of values, as described below.
*
* Text-based comparisons (EQUALS, IS ONE OF, etc.)
*
* - accept {@link String} values,
*
- all other values are automatically converted to string (a warning will be logged but evaluation will continue as normal).
*
*
* SemVer-based comparisons (IS ONE OF, <, >=, etc.)
*
* - accept {@link String} values containing a properly formatted, valid semver value,
*
- all other values are considered invalid (a warning will be logged and the currently evaluated targeting rule will be skipped).
*
*
* Number-based comparisons (=, <, >=, etc.)
*
* - accept {@link Double} values (except for {@code Double.NaN}) and all other numeric values which can safely be converted to {@link Double}
*
- accept {@link String} values containing a properly formatted, valid {@link Double} value
*
- all other values are considered invalid (a warning will be logged and the currently evaluated targeting rule will be skipped).
*
*
* Date time-based comparisons (BEFORE / AFTER)
*
* - accept {@link java.util.Date} values, which are automatically converted to a second-based Unix timestamp
*
- accept {@link java.time.Instant} values, which are automatically converted to a second-based Unix timestamp
*
- accept {@link Double} values (except for {@code Double.NaN}) representing a second-based Unix timestamp and all other numeric values which can safely be converted to {@link Double}
*
- accept {@link String} values containing a properly formatted, valid {@link Double} value
*
- all other values are considered invalid (a warning will be logged and the currently evaluated targeting rule will be skipped).
*
*
* String array-based comparisons (ARRAY CONTAINS ANY OF / ARRAY NOT CONTAINS ANY OF)
*
* - accept arrays of {@link String}
*
- accept {@link java.util.List} of {@link String}
*
- accept {@link String} values containing a valid JSON string which can be deserialized to an array of {@link String}
*
- all other values are considered invalid (a warning will be logged and the currently evaluated targeting rule will be skipped).
*
*
* In case a non-string attribute value needs to be converted to {@link String} during evaluation, it will always be done using the same format which is accepted by the comparisons.
*
* @param custom the custom attributes.
* @return the builder.
*/
public Builder custom(Map custom) {
this.custom = custom;
return this;
}
/**
* Builds the configured {@link User} instance.
*
* @param identifier the user identifier.
* @return the configured {@link User} instance.
*/
public User build(String identifier) {
return new User(identifier, this.email, this.country, this.custom);
}
}
}