Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config v6 #39

Merged
merged 59 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
5acb222
SDKKey validation updated to handle proxy and new key format
novalisdenahi Jul 24, 2023
3a9049c
Remove unnecessary closes
novalisdenahi Jul 24, 2023
651bdd3
Comparators rework
novalisdenahi Jul 26, 2023
e47f842
Fix logMatch for date comparator
novalisdenahi Jul 27, 2023
230153c
Fix typo
novalisdenahi Jul 27, 2023
721d75f
Fix comparators names
novalisdenahi Jul 28, 2023
ccfb3bb
v6 model added, evaluation and tests updated
novalisdenahi Aug 17, 2023
a153958
Comparator and other fixes
novalisdenahi Aug 18, 2023
e86d595
Implement evaluatePrerequisiteFlagCondition and added v6 tests
novalisdenahi Sep 7, 2023
5fb07b7
Fix comparator and other errors
novalisdenahi Sep 8, 2023
9f12280
Added segments impl and test
novalisdenahi Sep 12, 2023
8e819ea
Rename ComparisonCondition to UserCondition
novalisdenahi Sep 12, 2023
305bbfb
Added logging
novalisdenahi Sep 14, 2023
59bb057
Added evaluation logger and test to v6
novalisdenahi Sep 22, 2023
9e06ab0
Fix TODO-s
novalisdenahi Sep 28, 2023
da07006
CodeFormat
novalisdenahi Sep 28, 2023
abe46ba
Small fixes and Evaluation Logger run based on logLevel check.
novalisdenahi Sep 29, 2023
1a11c7f
Small fixes
novalisdenahi Sep 29, 2023
170f96b
Small fixes
novalisdenahi Oct 5, 2023
a8db982
v5 and v6 test sdkkeys updates
novalisdenahi Oct 5, 2023
a10bb75
Update logger turn off test
novalisdenahi Oct 5, 2023
f5f8529
User attributeValueFrom methods added.
novalisdenahi Oct 6, 2023
99ba136
Fix values in the json
novalisdenahi Oct 6, 2023
3cab307
Add some javadoc
novalisdenahi Oct 11, 2023
26bd704
Merge branch 'master' into config-v6
novalisdenahi Oct 11, 2023
dff579a
Fix test after merge
novalisdenahi Oct 11, 2023
c5acbc9
Fix list truncation
novalisdenahi Oct 17, 2023
d8ed26b
Bug and codesmell fixes
novalisdenahi Oct 24, 2023
0dfbcb9
Add text comparators and model changes.
novalisdenahi Oct 26, 2023
a6ec6d4
Test updates
novalisdenahi Oct 26, 2023
f1e6f13
Segment tests and evaluation update.
novalisdenahi Oct 31, 2023
49a35ce
Unicode test and fixes added
novalisdenahi Nov 6, 2023
cbc3322
Remove unnecessary byte array copy
novalisdenahi Nov 6, 2023
047433b
Code format
novalisdenahi Nov 6, 2023
7575e7e
Code format
novalisdenahi Nov 6, 2023
38a56bf
Code smell fixes
novalisdenahi Nov 7, 2023
26afa94
Add ConditionAccessor interface to make evaluateConditions method mor…
novalisdenahi Nov 9, 2023
0e84370
Update circular dependency check to throw error.
novalisdenahi Nov 23, 2023
4dba489
Reformat code
novalisdenahi Nov 23, 2023
ef737da
Small fixes and tests added
novalisdenahi Nov 30, 2023
812ac68
Merge remote-tracking branch 'origin/master' into config-v6
novalisdenahi Dec 4, 2023
f0013f0
Fix merge errors
novalisdenahi Dec 4, 2023
96ddd13
Fixes based on code review.
novalisdenahi Jan 10, 2024
e8dfbab
Fixes evaluationCondition default return value to true.
novalisdenahi Jan 10, 2024
cbcbbe4
Add Object as acceptable type.
novalisdenahi Jan 11, 2024
cc120fc
Fix user Identifier, Email, Country overrides.
novalisdenahi Jan 11, 2024
cce6f2b
Update getKeyAndValueFromSettingsMap to handle targeting rule percent…
novalisdenahi Jan 11, 2024
3fa66c1
1103 log message update.
novalisdenahi Jan 11, 2024
c265e0d
evaluateArrayContains null and empty values handed properly.
novalisdenahi Jan 11, 2024
7350c4d
Correct null handling of comparison values
adams85 Jan 11, 2024
3ac75b8
Minor refactor to improve code readability
adams85 Jan 11, 2024
99bb4ba
Add special character test.
novalisdenahi Jan 13, 2024
f7a0dfd
Remove Object from ConfigurationProvider docs.
novalisdenahi Jan 14, 2024
2366d7c
Move matrix resouces to matrix folder
novalisdenahi Jan 14, 2024
4e0fa32
Add comparators value trim test.
novalisdenahi Jan 14, 2024
24ae4f6
Add user value trim test.
novalisdenahi Jan 15, 2024
c75db42
Fixes based on code review
novalisdenahi Jan 16, 2024
8fdedbf
Fix docs based on code review
novalisdenahi Jan 16, 2024
8a7cb4b
Update version to 9.0.0
novalisdenahi Jan 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,15 @@ dependencies {
api 'com.google.code.gson:gson:2.9.0'
api 'commons-codec:commons-codec:1.15'
api 'de.skuzzle:semantic-version:2.1.0'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.1.0'
testImplementation 'org.slf4j:slf4j-simple:2.0.0'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0'
testImplementation 'ch.qos.logback:logback-classic:1.3.11'
testImplementation 'ch.qos.logback:logback-core:1.3.11'
testImplementation 'com.squareup.okhttp3:mockwebserver:4.11.0'
testImplementation 'org.mockito:mockito-core:4.2.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.1.0'
testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.8.0'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.10.0'

}

jar {
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/com/configcat/Condition.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.configcat;

import com.google.gson.annotations.SerializedName;

/**
* Represents a condition.
*/
public class Condition implements ConditionAccessor {
@SerializedName(value = "u")
private UserCondition userCondition;
@SerializedName(value = "s")
private SegmentCondition segmentCondition;
@SerializedName(value = "p")
private PrerequisiteFlagCondition prerequisiteFlagCondition;

@Override
public UserCondition getUserCondition() {
return userCondition;
}

@Override
public SegmentCondition getSegmentCondition() {
return segmentCondition;
}

@Override
public PrerequisiteFlagCondition getPrerequisiteFlagCondition() {
return prerequisiteFlagCondition;
}
}
12 changes: 12 additions & 0 deletions src/main/java/com/configcat/ConditionAccessor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.configcat;

/**
* The interface for the data classes which can be passed to the RolloutEvaluator.evaluateConditions method.
*/
public interface ConditionAccessor {
z4kn4fein marked this conversation as resolved.
Show resolved Hide resolved
UserCondition getUserCondition();

SegmentCondition getSegmentCondition();

PrerequisiteFlagCondition getPrerequisiteFlagCondition();
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,36 @@
import java.util.HashMap;
import java.util.Map;

class Config {
/**
* Details of a ConfigCat config.
*/
public class Config {

/**
* The config preferences.
*/
z4kn4fein marked this conversation as resolved.
Show resolved Hide resolved
@SerializedName(value = "p")
private Preferences preferences;
/**
* The map of settings.
*/
@SerializedName(value = "f")
private Map<String, Setting> entries = new HashMap<>();
private final Map<String, Setting> entries = new HashMap<>();

/**
* The list of segments.
*/
@SerializedName(value = "s")
private Segment[] segments;

public Preferences getPreferences() {
return preferences;
}

public Segment[] getSegments() {
return segments;
}

public Map<String, Setting> getEntries() {
return entries;
}
Expand All @@ -24,20 +44,4 @@ boolean isEmpty() {
}

public static final Config EMPTY = new Config();
}

class Preferences {
@SerializedName(value = "u")
private String baseUrl;
@SerializedName(value = "r")
private int redirect;

public String getBaseUrl() {
return baseUrl;
}

public int getRedirect() {
return redirect;
}
}

}
184 changes: 94 additions & 90 deletions src/main/java/com/configcat/ConfigCatClient.java

Large diffs are not rendered by default.

58 changes: 54 additions & 4 deletions src/main/java/com/configcat/ConfigCatLogMessages.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ final class ConfigCatLogMessages {
/**
* Log message for Fetch Failed Due To Unexpected error. The log eventId is 1103.
*/
public static final String FETCH_FAILED_DUE_TO_UNEXPECTED_ERROR = "Unexpected error occurred while trying to fetch config JSON.";
public static final String FETCH_FAILED_DUE_TO_UNEXPECTED_ERROR = "Unexpected error occurred while trying to fetch config JSON. It is most likely due to a local network issue. Please make sure your application can reach the ConfigCat CDN servers (or your proxy server) over HTTP.";

/**
* Log message for Fetch Failed Due To Invalid Sdk Key error. The log eventId is 1100.
Expand All @@ -46,7 +46,7 @@ private ConfigCatLogMessages() { /* prevent from instantiation*/ }
/**
* Log message for Config Json Is Not Presented errors when the method returns with default value. The log eventId is 1000.
*
* @param key The feature flag key.
* @param key The feature flag key.
* @param defaultParamName The default parameter name.
* @param defaultParamValue The default parameter value.
* @return The formatted error message.
Expand Down Expand Up @@ -188,15 +188,65 @@ public static String getClientIsAlreadyCreated(final String sdkKey) {
}

/**
* Log message for Targeting Is Not Possible warning. The log eventId 3001.
* Log message for User Object is missing warning. The log eventId 3001.
*
* @param key The feature flag setting key.
* @return The formatted warn message.
*/
public static String getTargetingIsNotPossible(final String key) {
public static String getUserObjectMissing(final String key) {
return "Cannot evaluate targeting rules and % options for setting '" + key + "' (User Object is missing). You should pass a User Object to the evaluation methods like `getValue()`/`getValueAsync()` in order to make targeting work properly. Read more: https://configcat.com/docs/advanced/user-object/";
}

/**
* Log message for User Attribute is missing warning. The log eventId 3003.
*
* @param key The feature flag setting key.
* @param userCondition The user condition where the attribute is checked.
* @param attributeName The user attribute name.
* @return The formatted warn message.
*/
public static String getUserAttributeMissing(final String key, final UserCondition userCondition, final String attributeName) {
return "Cannot evaluate condition (" + LogHelper.formatUserCondition(userCondition) + ") for setting '" + key + "' (the User." + attributeName + " attribute is missing). You should set the User." + attributeName + " attribute in order to make targeting work properly. Read more: https://configcat.com/docs/advanced/user-object/";
}

/**
* Log message for User Attribute is missing warning. The log eventId 3003.
*
* @param key The feature flag setting key.
* @param attributeName The user attribute name.
* @return The formatted warn message.
*/
public static String getUserAttributeMissing(final String key, final String attributeName) {
return "Cannot evaluate % options for setting '" + key + "' (the User." + attributeName + " attribute is missing). You should set the User." + attributeName + " attribute in order to make targeting work properly. Read more: https://configcat.com/docs/advanced/user-object/";
}

/**
* Log message for User Attribute is invalid warning. The log eventId 3004.
*
* @param key The feature flag setting key.
* @param userCondition The user condition where the attribute is checked.
* @param reason Why the attribute is invalid.
* @param attributeName The user attribute name.
* @return The formatted warn message.
*/
public static String getUserAttributeInvalid(final String key, final UserCondition userCondition, final String reason, final String attributeName) {
return "Cannot evaluate condition (" + LogHelper.formatUserCondition(userCondition) + ") for setting '" + key + "' (" + reason + "). Please check the User." + attributeName + " attribute and make sure that its value corresponds to the comparison operator.";
}


/**
* Log message for User Attribute value is automatically converted warning. The log eventId 3005.
*
* @param key The feature flag setting key.
* @param userCondition The condition where the circularity is detected.
* @param attributeName The user attribute name.
* @param attributeValue The user attribute value.
* @return The formatted warn message.
*/
public static String getUserObjectAttributeIsAutoConverted(String key, UserCondition userCondition, String attributeName, String attributeValue) {
return "Evaluation of condition (" + LogHelper.formatUserCondition(userCondition) + ") for setting '" + key + "' may not produce the expected result (the User." + attributeName + " attribute is not a string value, thus it was automatically converted to the string value '" + attributeValue + "'). Please make sure that using a non-string value was intended.";
}

/**
* Log message for Config Service Method Has No Effect Due To Closed Client warning. The log eventId 3201.
*
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/com/configcat/ConfigCatLogger.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,26 @@ public void warn(int eventId, String message) {
public void error(int eventId, String message, Exception exception) {
if (this.configCatHooks != null) this.configCatHooks.invokeOnError(message);
if (this.logLevel.ordinal() <= LogLevel.ERROR.ordinal()) {
this.logger.error("[{}] {}",eventId, message, exception);
this.logger.error("[{}] {}", eventId, message, exception);
}
}

public void error(int eventId, String message) {
if (this.configCatHooks != null) this.configCatHooks.invokeOnError(message);
if (this.logLevel.ordinal() <= LogLevel.ERROR.ordinal()) {
this.logger.error("[{}] {}",eventId, message);
this.logger.error("[{}] {}", eventId, message);
}
}

public void info(int eventId, String message) {
if (this.logLevel.ordinal() <= LogLevel.INFO.ordinal()) {
this.logger.info("[{}] {}",eventId, message);
this.logger.info("[{}] {}", eventId, message);
}
}

public void debug(String message) {
if (this.logLevel.ordinal() <= LogLevel.DEBUG.ordinal()) {
this.logger.debug("[{}] {}",0, message);
this.logger.debug("[{}] {}", 0, message);
}
}
}
5 changes: 2 additions & 3 deletions src/main/java/com/configcat/ConfigFetcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,8 @@ private CompletableFuture<FetchResponse> executeFetchAsync(int executionCount, S
if (config.getPreferences() == null) {
return CompletableFuture.completedFuture(fetchResponse);
}

String newUrl = config.getPreferences().getBaseUrl();
if (newUrl.equals(this.url)) {
if (newUrl == null || newUrl.isEmpty() || this.url.equals(newUrl)) {
return CompletableFuture.completedFuture(fetchResponse);
}

Expand Down Expand Up @@ -178,7 +177,7 @@ Request getRequest(String etag) {

private Result<Config> deserializeConfig(String json) {
try {
return Result.success(Utils.gson.fromJson(json, Config.class));
return Result.success(Utils.deserializeConfig(json));
} catch (Exception e) {
String message = ConfigCatLogMessages.FETCH_RECEIVED_200_WITH_INVALID_BODY_ERROR;
this.logger.error(1105, message, e);
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/configcat/ConfigurationProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ public interface ConfigurationProvider extends Closeable {
/**
* Gets the key of a setting and its value identified by the given Variation ID (analytics).
*
* @param classOfT the class of T. Only {@link String}, {@link Integer}, {@link Double} or {@link Boolean} types are supported.
* @param classOfT the class of T. Only {@link String}, {@link Integer}, {@link Double} or {@link Boolean} types are supported.
* @param variationId the Variation ID.
* @param <T> the type of the desired feature flag or setting.
* @return the key of a setting and its value.
Expand Down
9 changes: 7 additions & 2 deletions src/main/java/com/configcat/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ private Constants() { /* prevent from instantiation*/ }

static final long DISTANT_FUTURE = Long.MAX_VALUE;
static final long DISTANT_PAST = 0;
static final String CONFIG_JSON_NAME = "config_v5.json";
static final String CONFIG_JSON_NAME = "config_v6.json";
static final String SERIALIZATION_FORMAT_VERSION = "v2";

static final String VERSION = "8.4.0";

static final String SDK_KEY_PROXY_PREFIX = "configcat-proxy/";
static final String SDK_KEY_PREFIX = "configcat-sdk-1";

static final int SDK_KEY_SECTION_LENGTH = 22;

}
21 changes: 21 additions & 0 deletions src/main/java/com/configcat/DateTimeUtils.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package com.configcat;

import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Date;
import java.util.TimeZone;

public class DateTimeUtils {

private DateTimeUtils() { /* prevent from instantiation*/ }
Expand All @@ -12,4 +17,20 @@ public static boolean isValidDate(String date) {
}
return true;
}

public static String doubleToFormattedUTC(double dateInDouble) {
long dateInMillisecond = (long) dateInDouble * 1000;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
return simpleDateFormat.format(new Date(dateInMillisecond));
}


public static double getUnixSeconds(Date date) {
return date.getTime() / 1000d;
}

public static double getUnixSeconds(Instant date) {
return date.toEpochMilli() / 1000d;
}
}
Loading