diff --git a/CHANGES.txt b/CHANGES.txt
index ec4d34a39..0d1f612ea 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,10 @@
+4.12.0 (May 15, 2024)
+- Added support for targeting rules based on semantic versions (https://semver.org/).
+- Added the logic to handle correctly when the SDK receives an unsupported Matcher type.
+- Enhanced SDK Headers for Authorization Frameworks
+- Cleaned unused imports and renaming some methods
+- Fixed empty token handler thanks to @hpark-miovision
+
4.11.1 (Feb 29, 2024)
- Fixed deadlock in UniqueKeysTracker when sending Unique Keys.
@@ -9,7 +16,7 @@
- Added getTreatmentsByFlagSets without attributes.
- Fixed some issues for flag sets: Not logging a warning when using flag sets that don't contain cached feature flags.
-4.10.1 (Nov 9, 2023)
+4.10.1 (Nov 8, 2023)
- Fixed handler for response http headers.
4.10.0 (Nov 2, 2023)
diff --git a/client/pom.xml b/client/pom.xml
index 131fe248c..b48aa1100 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -5,7 +5,7 @@
io.split.client
java-client-parent
- 4.11.1
+ 4.12.0
java-client
jar
diff --git a/client/src/main/java/io/split/Spec.java b/client/src/main/java/io/split/Spec.java
new file mode 100644
index 000000000..9e03a59ab
--- /dev/null
+++ b/client/src/main/java/io/split/Spec.java
@@ -0,0 +1,11 @@
+package io.split;
+
+public final class Spec {
+
+ private Spec() {
+ // restrict instantiation
+ }
+
+ public static final String SPEC_VERSION = "1.1";
+}
+
diff --git a/client/src/main/java/io/split/client/CustomHeaderDecorator.java b/client/src/main/java/io/split/client/CustomHeaderDecorator.java
new file mode 100644
index 000000000..934c43681
--- /dev/null
+++ b/client/src/main/java/io/split/client/CustomHeaderDecorator.java
@@ -0,0 +1,15 @@
+package io.split.client;
+
+import io.split.client.dtos.RequestContext;
+
+import java.util.Map;
+import java.util.List;
+
+public interface CustomHeaderDecorator
+{
+ /**
+ * Get the additional headers needed for all http operations
+ * @return HashMap of addition headers
+ */
+ Map> getHeaderOverrides(RequestContext context);
+}
diff --git a/client/src/main/java/io/split/client/HttpSegmentChangeFetcher.java b/client/src/main/java/io/split/client/HttpSegmentChangeFetcher.java
index 84bf09509..bd365590c 100644
--- a/client/src/main/java/io/split/client/HttpSegmentChangeFetcher.java
+++ b/client/src/main/java/io/split/client/HttpSegmentChangeFetcher.java
@@ -2,26 +2,23 @@
import com.google.common.annotations.VisibleForTesting;
import io.split.client.dtos.SegmentChange;
+import io.split.client.dtos.SplitHttpResponse;
import io.split.client.utils.Json;
import io.split.client.utils.Utils;
import io.split.engine.common.FetchOptions;
import io.split.engine.segments.SegmentChangeFetcher;
+import io.split.service.SplitHttpClient;
import io.split.telemetry.domain.enums.HTTPLatenciesEnum;
import io.split.telemetry.domain.enums.LastSynchronizationRecordsEnum;
import io.split.telemetry.domain.enums.ResourceEnum;
import io.split.telemetry.storage.TelemetryRuntimeProducer;
-import org.apache.hc.client5.http.classic.methods.HttpGet;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.HttpStatus;
-import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.net.URIBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.net.URISyntaxException;
-import java.nio.charset.StandardCharsets;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -33,20 +30,17 @@ public final class HttpSegmentChangeFetcher implements SegmentChangeFetcher {
private static final String SINCE = "since";
private static final String TILL = "till";
- private static final String PREFIX = "segmentChangeFetcher";
- private static final String CACHE_CONTROL_HEADER_NAME = "Cache-Control";
- private static final String CACHE_CONTROL_HEADER_VALUE = "no-cache";
- private final CloseableHttpClient _client;
+ private final SplitHttpClient _client;
private final URI _target;
private final TelemetryRuntimeProducer _telemetryRuntimeProducer;
- public static HttpSegmentChangeFetcher create(CloseableHttpClient client, URI root, TelemetryRuntimeProducer telemetryRuntimeProducer)
+ public static HttpSegmentChangeFetcher create(SplitHttpClient client, URI root, TelemetryRuntimeProducer telemetryRuntimeProducer)
throws URISyntaxException {
return new HttpSegmentChangeFetcher(client, Utils.appendPath(root, "api/segmentChanges"), telemetryRuntimeProducer);
}
- private HttpSegmentChangeFetcher(CloseableHttpClient client, URI uri, TelemetryRuntimeProducer telemetryRuntimeProducer) {
+ private HttpSegmentChangeFetcher(SplitHttpClient client, URI uri, TelemetryRuntimeProducer telemetryRuntimeProducer) {
_client = client;
_target = uri;
checkNotNull(_target);
@@ -57,8 +51,6 @@ private HttpSegmentChangeFetcher(CloseableHttpClient client, URI uri, TelemetryR
public SegmentChange fetch(String segmentName, long since, FetchOptions options) {
long start = System.currentTimeMillis();
- CloseableHttpResponse response = null;
-
try {
String path = _target.getPath() + "/" + segmentName;
URIBuilder uriBuilder = new URIBuilder(_target)
@@ -69,45 +61,27 @@ public SegmentChange fetch(String segmentName, long since, FetchOptions options)
}
URI uri = uriBuilder.build();
- HttpGet request = new HttpGet(uri);
-
- if(options.cacheControlHeadersEnabled()) {
- request.setHeader(CACHE_CONTROL_HEADER_NAME, CACHE_CONTROL_HEADER_VALUE);
- }
-
- response = _client.execute(request);
-
- int statusCode = response.getCode();
- if (_log.isDebugEnabled()) {
- _log.debug(String.format("[%s] %s. Status code: %s", request.getMethod(), uri.toURL(), statusCode));
- }
+ SplitHttpResponse response = _client.get(uri, options, null);
- if (statusCode < HttpStatus.SC_OK || statusCode >= HttpStatus.SC_MULTIPLE_CHOICES) {
- _telemetryRuntimeProducer.recordSyncError(ResourceEnum.SEGMENT_SYNC, statusCode);
- _log.error(String.format("Response status was: %s. Reason: %s", statusCode , response.getReasonPhrase()));
- if (statusCode == HttpStatus.SC_FORBIDDEN) {
+ if (response.statusCode() < HttpStatus.SC_OK || response.statusCode() >= HttpStatus.SC_MULTIPLE_CHOICES) {
+ _telemetryRuntimeProducer.recordSyncError(ResourceEnum.SEGMENT_SYNC, response.statusCode());
+ if (response.statusCode() == HttpStatus.SC_FORBIDDEN) {
_log.error("factory instantiation: you passed a client side type sdkKey, " +
"please grab an sdk key from the Split user interface that is of type server side");
}
throw new IllegalStateException(String.format("Could not retrieve segment changes for %s, since %s; http return code %s",
- segmentName, since, statusCode));
+ segmentName, since, response.statusCode()));
}
-
_telemetryRuntimeProducer.recordSuccessfulSync(LastSynchronizationRecordsEnum.SEGMENTS, System.currentTimeMillis());
- String json = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
-
- return Json.fromJson(json, SegmentChange.class);
+ return Json.fromJson(response.body(), SegmentChange.class);
} catch (Exception e) {
throw new IllegalStateException(String.format("Error occurred when trying to sync segment: %s, since: %s. Details: %s",
segmentName, since, e), e);
} finally {
_telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.SEGMENTS, System.currentTimeMillis()-start);
- Utils.forceClose(response);
}
-
-
}
@VisibleForTesting
diff --git a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java
index 65d37d144..9f8d2036b 100644
--- a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java
+++ b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java
@@ -1,29 +1,28 @@
package io.split.client;
import com.google.common.annotations.VisibleForTesting;
+
import io.split.client.dtos.SplitChange;
+import io.split.client.dtos.SplitHttpResponse;
import io.split.client.exceptions.UriTooLongException;
import io.split.client.utils.Json;
import io.split.client.utils.Utils;
import io.split.engine.common.FetchOptions;
import io.split.engine.experiments.SplitChangeFetcher;
+import io.split.service.SplitHttpClient;
import io.split.telemetry.domain.enums.HTTPLatenciesEnum;
import io.split.telemetry.domain.enums.ResourceEnum;
import io.split.telemetry.storage.TelemetryRuntimeProducer;
-import org.apache.hc.client5.http.classic.methods.HttpGet;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.HttpStatus;
-import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.net.URIBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.net.URISyntaxException;
-import java.nio.charset.StandardCharsets;
import static com.google.common.base.Preconditions.checkNotNull;
+import static io.split.Spec.SPEC_VERSION;
/**
* Created by adilaijaz on 5/30/15.
@@ -34,20 +33,17 @@ public final class HttpSplitChangeFetcher implements SplitChangeFetcher {
private static final String SINCE = "since";
private static final String TILL = "till";
private static final String SETS = "sets";
-
- private static final String HEADER_CACHE_CONTROL_NAME = "Cache-Control";
- private static final String HEADER_CACHE_CONTROL_VALUE = "no-cache";
-
- private final CloseableHttpClient _client;
+ private static final String SPEC = "s";
+ private final SplitHttpClient _client;
private final URI _target;
private final TelemetryRuntimeProducer _telemetryRuntimeProducer;
- public static HttpSplitChangeFetcher create(CloseableHttpClient client, URI root, TelemetryRuntimeProducer telemetryRuntimeProducer)
+ public static HttpSplitChangeFetcher create(SplitHttpClient client, URI root, TelemetryRuntimeProducer telemetryRuntimeProducer)
throws URISyntaxException {
return new HttpSplitChangeFetcher(client, Utils.appendPath(root, "api/splitChanges"), telemetryRuntimeProducer);
}
- private HttpSplitChangeFetcher(CloseableHttpClient client, URI uri, TelemetryRuntimeProducer telemetryRuntimeProducer) {
+ private HttpSplitChangeFetcher(SplitHttpClient client, URI uri, TelemetryRuntimeProducer telemetryRuntimeProducer) {
_client = client;
_target = uri;
checkNotNull(_target);
@@ -64,49 +60,34 @@ public SplitChange fetch(long since, FetchOptions options) {
long start = System.currentTimeMillis();
- CloseableHttpResponse response = null;
-
try {
- URIBuilder uriBuilder = new URIBuilder(_target).addParameter(SINCE, "" + since);
- if (options.hasCustomCN()) {
- uriBuilder.addParameter(TILL, "" + options.targetCN());
- }
+ URIBuilder uriBuilder = new URIBuilder(_target).addParameter(SPEC, "" + SPEC_VERSION);
+ uriBuilder.addParameter(SINCE, "" + since);
if (!options.flagSetsFilter().isEmpty()) {
uriBuilder.addParameter(SETS, "" + options.flagSetsFilter());
}
- URI uri = uriBuilder.build();
-
- HttpGet request = new HttpGet(uri);
- if(options.cacheControlHeadersEnabled()) {
- request.setHeader(HEADER_CACHE_CONTROL_NAME, HEADER_CACHE_CONTROL_VALUE);
- }
-
- response = _client.execute(request);
-
- int statusCode = response.getCode();
-
- if (_log.isDebugEnabled()) {
- _log.debug(String.format("[%s] %s. Status code: %s", request.getMethod(), uri.toURL(), statusCode));
+ if (options.hasCustomCN()) {
+ uriBuilder.addParameter(TILL, "" + options.targetCN());
}
+ URI uri = uriBuilder.build();
+ SplitHttpResponse response = _client.get(uri, options, null);
- if (statusCode < HttpStatus.SC_OK || statusCode >= HttpStatus.SC_MULTIPLE_CHOICES) {
- _telemetryRuntimeProducer.recordSyncError(ResourceEnum.SPLIT_SYNC, statusCode);
- if (statusCode == HttpStatus.SC_REQUEST_URI_TOO_LONG) {
+ if (response.statusCode() < HttpStatus.SC_OK || response.statusCode() >= HttpStatus.SC_MULTIPLE_CHOICES) {
+ if (response.statusCode() == HttpStatus.SC_REQUEST_URI_TOO_LONG) {
_log.error("The amount of flag sets provided are big causing uri length error.");
- throw new UriTooLongException(String.format("Status code: %s. Message: %s", statusCode, response.getReasonPhrase()));
+ throw new UriTooLongException(String.format("Status code: %s. Message: %s", response.statusCode(), response.statusMessage()));
}
- _log.warn(String.format("Response status was: %s. Reason: %s", statusCode , response.getReasonPhrase()));
- throw new IllegalStateException(String.format("Could not retrieve splitChanges since %s; http return code %s", since, statusCode));
+ _telemetryRuntimeProducer.recordSyncError(ResourceEnum.SPLIT_SYNC, response.statusCode());
+ throw new IllegalStateException(
+ String.format("Could not retrieve splitChanges since %s; http return code %s", since, response.statusCode())
+ );
}
- String json = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
-
- return Json.fromJson(json, SplitChange.class);
+ return Json.fromJson(response.body(), SplitChange.class);
} catch (Exception e) {
throw new IllegalStateException(String.format("Problem fetching splitChanges since %s: %s", since, e), e);
} finally {
_telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.SPLITS, System.currentTimeMillis()-start);
- Utils.forceClose(response);
}
}
diff --git a/client/src/main/java/io/split/client/NoOpHeaderDecorator.java b/client/src/main/java/io/split/client/NoOpHeaderDecorator.java
new file mode 100644
index 000000000..8ce04fdbc
--- /dev/null
+++ b/client/src/main/java/io/split/client/NoOpHeaderDecorator.java
@@ -0,0 +1,18 @@
+package io.split.client;
+
+import io.split.client.CustomHeaderDecorator;
+import io.split.client.dtos.RequestContext;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+class NoOpHeaderDecorator implements CustomHeaderDecorator {
+ public NoOpHeaderDecorator() {
+ }
+
+ @Override
+ public Map> getHeaderOverrides(RequestContext context) {
+ return new HashMap<>();
+ }
+}
diff --git a/client/src/main/java/io/split/client/RequestDecorator.java b/client/src/main/java/io/split/client/RequestDecorator.java
new file mode 100644
index 000000000..1572463ef
--- /dev/null
+++ b/client/src/main/java/io/split/client/RequestDecorator.java
@@ -0,0 +1,77 @@
+package io.split.client;
+
+import io.split.client.dtos.RequestContext;
+
+import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.Header;
+
+import java.util.HashSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.List;
+
+public final class RequestDecorator {
+ CustomHeaderDecorator _headerDecorator;
+
+ private static final Set forbiddenHeaders = new HashSet<>(Arrays.asList(
+ "splitsdkversion",
+ "splitmachineip",
+ "splitmachinename",
+ "splitimpressionsmode",
+ "host",
+ "referrer",
+ "content-type",
+ "content-length",
+ "content-encoding",
+ "accept",
+ "keep-alive",
+ "x-fastly-debug"));
+
+ public RequestDecorator(CustomHeaderDecorator headerDecorator) {
+ _headerDecorator = (headerDecorator == null)
+ ? new NoOpHeaderDecorator()
+ : headerDecorator;
+ }
+
+ public HttpRequest decorateHeaders(HttpRequest request) {
+ try {
+ Map> headers = _headerDecorator
+ .getHeaderOverrides(new RequestContext(convertToMap(request.getHeaders())));
+ for (Map.Entry> entry : headers.entrySet()) {
+ if (isHeaderAllowed(entry.getKey())) {
+ List values = entry.getValue();
+ for (int i = 0; i < values.size(); i++) {
+ if (i == 0) {
+ request.setHeader(entry.getKey(), values.get(i));
+ } else {
+ request.addHeader(entry.getKey(), values.get(i));
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ throw new IllegalArgumentException(
+ String.format("Problem adding custom headers to request decorator: %s", e), e);
+ }
+
+ return request;
+ }
+
+ private boolean isHeaderAllowed(String headerName) {
+ return !forbiddenHeaders.contains(headerName.toLowerCase());
+ }
+
+ private Map> convertToMap(Header[] to_convert) {
+ Map> to_return = new HashMap>();
+ for (Integer i = 0; i < to_convert.length; i++) {
+ if (!to_return.containsKey(to_convert[i].getName())) {
+ to_return.put(to_convert[i].getName(), new ArrayList());
+ }
+ to_return.get(to_convert[i].getName()).add(to_convert[i].getValue());
+ }
+ return to_return;
+ }
+}
diff --git a/client/src/main/java/io/split/client/SplitClientConfig.java b/client/src/main/java/io/split/client/SplitClientConfig.java
index 11e2ec2bc..2f29c1719 100644
--- a/client/src/main/java/io/split/client/SplitClientConfig.java
+++ b/client/src/main/java/io/split/client/SplitClientConfig.java
@@ -13,9 +13,9 @@
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
-import java.io.InputStream;
import java.util.Properties;
import java.util.concurrent.ThreadFactory;
+import java.io.InputStream;
import static io.split.inputValidation.FlagSetsValidator.cleanup;
@@ -90,6 +90,8 @@ public class SplitClientConfig {
private final long _lastSeenCacheSize;
private final HashSet _flagSetsFilter;
private final int _invalidSets;
+ private final CustomHeaderDecorator _customHeaderDecorator;
+
public static Builder builder() {
return new Builder();
@@ -145,7 +147,8 @@ private SplitClientConfig(String endpoint,
long lastSeenCacheSize,
ThreadFactory threadFactory,
HashSet flagSetsFilter,
- int invalidSets) {
+ int invalidSets,
+ CustomHeaderDecorator customHeaderDecorator) {
_endpoint = endpoint;
_eventsEndpoint = eventsEndpoint;
_featuresRefreshRate = pollForFeatureChangesEveryNSeconds;
@@ -197,6 +200,7 @@ private SplitClientConfig(String endpoint,
_threadFactory = threadFactory;
_flagSetsFilter = flagSetsFilter;
_invalidSets = invalidSets;
+ _customHeaderDecorator = customHeaderDecorator;
Properties props = new Properties();
try {
@@ -355,9 +359,17 @@ public String telemetryURL() {
return _telemetryURL;
}
+ /**
+ * @deprecated As of release 4.X.X, replaced by {@link #getTelemetryRefreshRate()} } //todo update version
+ **/
+ @Deprecated
public int get_telemetryRefreshRate() {
return _telemetryRefreshRate;
}
+
+ public int getTelemetryRefreshRate() {
+ return _telemetryRefreshRate;
+ }
public int streamingRetryDelay() {return _onDemandFetchRetryDelayMs;}
public int streamingFetchMaxRetries() {return _onDemandFetchMaxRetries;}
@@ -393,6 +405,10 @@ public int getInvalidSets() {
return _invalidSets;
}
+ public CustomHeaderDecorator customHeaderDecorator() {
+ return _customHeaderDecorator;
+ }
+
public static final class Builder {
private String _endpoint = SDK_ENDPOINT;
@@ -449,6 +465,7 @@ public static final class Builder {
private ThreadFactory _threadFactory;
private HashSet _flagSetsFilter = new HashSet<>();
private int _invalidSetsCount = 0;
+ private CustomHeaderDecorator _customHeaderDecorator = null;
public Builder() {
}
@@ -932,6 +949,17 @@ public Builder flagSetsFilter(List flagSetsFilter) {
return this;
}
+ /**
+ * User Custom Header Decorator
+ *
+ * @param customHeaderDecorator
+ * @return this builder
+ */
+ public Builder customHeaderDecorator(CustomHeaderDecorator customHeaderDecorator) {
+ _customHeaderDecorator = customHeaderDecorator;
+ return this;
+ }
+
/**
* Thread Factory
*
@@ -1091,7 +1119,8 @@ public SplitClientConfig build() {
_lastSeenCacheSize,
_threadFactory,
_flagSetsFilter,
- _invalidSetsCount);
+ _invalidSetsCount,
+ _customHeaderDecorator);
}
}
}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/client/SplitFactoryImpl.java b/client/src/main/java/io/split/client/SplitFactoryImpl.java
index 9adfc55e3..a783b1445 100644
--- a/client/src/main/java/io/split/client/SplitFactoryImpl.java
+++ b/client/src/main/java/io/split/client/SplitFactoryImpl.java
@@ -26,7 +26,6 @@
import io.split.client.impressions.strategy.ProcessImpressionNone;
import io.split.client.impressions.strategy.ProcessImpressionOptimized;
import io.split.client.impressions.strategy.ProcessImpressionStrategy;
-import io.split.client.interceptors.AuthorizationInterceptorFilter;
import io.split.client.interceptors.ClientKeyInterceptorFilter;
import io.split.client.interceptors.FlagSetsFilter;
import io.split.client.interceptors.FlagSetsFilterImpl;
@@ -58,6 +57,8 @@
import io.split.engine.segments.SegmentChangeFetcher;
import io.split.engine.segments.SegmentSynchronizationTaskImp;
import io.split.integrations.IntegrationsConfig;
+import io.split.service.SplitHttpClient;
+import io.split.service.SplitHttpClientImpl;
import io.split.storages.SegmentCache;
import io.split.storages.SegmentCacheConsumer;
import io.split.storages.SegmentCacheProducer;
@@ -120,8 +121,9 @@
public class SplitFactoryImpl implements SplitFactory {
private static final Logger _log = LoggerFactory.getLogger(SplitFactory.class);
- private static final String LEGACY_LOG_MESSAGE = "The sdk initialize in localhost mode using Legacy file. The splitFile or " +
- "inputStream doesn't add it to the config.";
+ private static final String LEGACY_LOG_MESSAGE = "The sdk initialize in localhost mode using Legacy file. The splitFile or "
+ +
+ "inputStream doesn't add it to the config.";
private final static long SSE_CONNECT_TIMEOUT = 30000;
private final static long SSE_SOCKET_TIMEOUT = 70000;
@@ -134,7 +136,7 @@ public class SplitFactoryImpl implements SplitFactory {
private final SplitClient _client;
private final SplitManager _manager;
- //Cache
+ // Cache
private final SplitCacheConsumer _splitCache;
private final SegmentCacheConsumer _segmentCache;
@@ -146,21 +148,21 @@ public class SplitFactoryImpl implements SplitFactory {
private final SDKMetadata _sdkMetadata;
private OperationMode _operationMode;
- //Depending on mode are not mandatory
+ // Depending on mode are not mandatory
private final TelemetrySyncTask _telemetrySyncTask;
private final SegmentSynchronizationTaskImp _segmentSynchronizationTaskImp;
private final SplitFetcher _splitFetcher;
private final SplitSynchronizationTask _splitSynchronizationTask;
private final EventsTask _eventsTask;
private final SyncManager _syncManager;
- private final CloseableHttpClient _httpclient;
+ private final SplitHttpClient _splitHttpClient;
private final UserStorageWrapper _userStorageWrapper;
private final ImpressionsSender _impressionsSender;
private final URI _rootTarget;
private final URI _eventsRootTarget;
private final UniqueKeysTracker _uniqueKeysTracker;
- //Constructor for standalone mode
+ // Constructor for standalone mode
public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyntaxException {
_userStorageWrapper = null;
_operationMode = config.operationMode();
@@ -174,16 +176,19 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
_telemetryStorageProducer = telemetryStorage;
if (config.blockUntilReady() == -1) {
- //BlockUntilReady not been set
- _log.warn("no setBlockUntilReadyTimeout parameter has been set - incorrect control treatments could be logged” " +
- "if no ready config has been set when building factory");
+ // BlockUntilReady not been set
+ _log.warn(
+ "no setBlockUntilReadyTimeout parameter has been set - incorrect control treatments could be logged” "
+ +
+ "if no ready config has been set when building factory");
}
// SDKReadinessGates
_gates = new SDKReadinessGates();
// HttpClient
- _httpclient = buildHttpClient(apiToken, config, _sdkMetadata);
+ RequestDecorator requestDecorator = new RequestDecorator(config.customHeaderDecorator());
+ _splitHttpClient = buildSplitHttpClient(apiToken, config, _sdkMetadata, requestDecorator);
// Roots
_rootTarget = URI.create(config.endpoint());
@@ -196,7 +201,8 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
ImpressionsStorage impressionsStorage = new InMemoryImpressionsStorage(config.impressionsQueueSize());
_splitCache = splitCache;
_segmentCache = segmentCache;
- _telemetrySynchronizer = new TelemetryInMemorySubmitter(_httpclient, URI.create(config.telemetryURL()), telemetryStorage,
+ _telemetrySynchronizer = new TelemetryInMemorySubmitter(_splitHttpClient, URI.create(config.telemetryURL()),
+ telemetryStorage,
splitCache, _segmentCache, telemetryStorage, _startTime);
// Segments
@@ -212,11 +218,12 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
config.featuresRefreshRate(),
config.getThreadFactory());
- //ImpressionSender
- _impressionsSender = HttpImpressionsSender.create(_httpclient, URI.create(config.eventsEndpoint()), config.impressionsMode(),
+ // ImpressionSender
+ _impressionsSender = HttpImpressionsSender.create(_splitHttpClient, URI.create(config.eventsEndpoint()),
+ config.impressionsMode(),
_telemetryStorageProducer);
- //UniqueKeysTracker
+ // UniqueKeysTracker
_uniqueKeysTracker = createUniqueKeysTracker(config);
// Impressions
@@ -224,10 +231,10 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
// EventClient
EventsStorage eventsStorage = new InMemoryEventsStorage(config.eventsQueueSize(), _telemetryStorageProducer);
- EventsSender eventsSender = EventsSender.create(_httpclient, _eventsRootTarget, _telemetryStorageProducer);
- _eventsTask = EventsTask.create(config.eventSendIntervalInMillis(), eventsStorage, eventsSender, config.getThreadFactory());
-
- _telemetrySyncTask = new TelemetrySyncTask(config.get_telemetryRefreshRate(), _telemetrySynchronizer, config.getThreadFactory());
+ EventsSender eventsSender = EventsSender.create(_splitHttpClient, _eventsRootTarget, _telemetryStorageProducer);
+ _eventsTask = EventsTask.create(config.eventSendIntervalInMillis(), eventsStorage, eventsSender,
+ config.getThreadFactory());
+ _telemetrySyncTask = new TelemetrySyncTask(config.getTelemetryRefreshRate(), _telemetrySynchronizer, config.getThreadFactory());
// Evaluator
_evaluator = new EvaluatorImp(splitCache, segmentCache);
@@ -240,8 +247,8 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
config,
_gates,
_evaluator,
- _telemetryStorageProducer, //TelemetryEvaluation instance
- _telemetryStorageProducer, //TelemetryConfiguration instance
+ _telemetryStorageProducer, // TelemetryEvaluation instance
+ _telemetryStorageProducer, // TelemetryConfiguration instance
flagSetsFilter);
// SplitManager
@@ -250,10 +257,11 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
// SyncManager
SplitTasks splitTasks = SplitTasks.build(_splitSynchronizationTask, _segmentSynchronizationTaskImp,
_impressionsManager, _eventsTask, _telemetrySyncTask, _uniqueKeysTracker);
- SplitAPI splitAPI = SplitAPI.build(_httpclient, buildSSEdHttpClient(apiToken, config, _sdkMetadata));
+ SplitAPI splitAPI = SplitAPI.build(_splitHttpClient, buildSSEdHttpClient(apiToken, config, _sdkMetadata), requestDecorator);
_syncManager = SyncManagerImp.build(splitTasks, _splitFetcher, splitCache, splitAPI,
- segmentCache, _gates, _telemetryStorageProducer, _telemetrySynchronizer, config, splitParser, flagSetsFilter);
+ segmentCache, _gates, _telemetryStorageProducer, _telemetrySynchronizer, config, splitParser,
+ flagSetsFilter);
_syncManager.start();
// DestroyOnShutDown
@@ -267,26 +275,32 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
}
}
- //Constructor for consumer mode
- protected SplitFactoryImpl(String apiToken, SplitClientConfig config, CustomStorageWrapper customStorageWrapper) throws URISyntaxException {
- //Variables that are not used in Consumer mode.
+ // Constructor for consumer mode
+ protected SplitFactoryImpl(String apiToken, SplitClientConfig config, CustomStorageWrapper customStorageWrapper)
+ throws URISyntaxException {
+ // Variables that are not used in Consumer mode.
_segmentSynchronizationTaskImp = null;
_splitFetcher = null;
_splitSynchronizationTask = null;
_eventsTask = null;
- _httpclient = null;
+ _splitHttpClient = null;
_rootTarget = null;
_eventsRootTarget = null;
Metadata metadata = new Metadata(config.ipAddressEnabled(), SplitClientConfig.splitSdkVersion);
_userStorageWrapper = new UserStorageWrapper(customStorageWrapper);
- UserCustomSegmentAdapterConsumer userCustomSegmentAdapterConsumer= new UserCustomSegmentAdapterConsumer(customStorageWrapper);
- UserCustomSplitAdapterConsumer userCustomSplitAdapterConsumer = new UserCustomSplitAdapterConsumer(customStorageWrapper);
- // TODO migrate impressions sender to Task instead manager and not instantiate Producer here.
+ UserCustomSegmentAdapterConsumer userCustomSegmentAdapterConsumer = new UserCustomSegmentAdapterConsumer(
+ customStorageWrapper);
+ UserCustomSplitAdapterConsumer userCustomSplitAdapterConsumer = new UserCustomSplitAdapterConsumer(
+ customStorageWrapper);
+ // TODO migrate impressions sender to Task instead manager and not instantiate
+ // Producer here.
UserCustomImpressionAdapterConsumer userCustomImpressionAdapterConsumer = new UserCustomImpressionAdapterConsumer();
- UserCustomImpressionAdapterProducer userCustomImpressionAdapterProducer = new UserCustomImpressionAdapterProducer(customStorageWrapper,
+ UserCustomImpressionAdapterProducer userCustomImpressionAdapterProducer = new UserCustomImpressionAdapterProducer(
+ customStorageWrapper,
metadata);
- UserCustomEventAdapterProducer userCustomEventAdapterProducer = new UserCustomEventAdapterProducer(customStorageWrapper, metadata);
+ UserCustomEventAdapterProducer userCustomEventAdapterProducer = new UserCustomEventAdapterProducer(
+ customStorageWrapper, metadata);
_operationMode = config.operationMode();
_sdkMetadata = createSdkMetadata(config.ipAddressEnabled(), SplitClientConfig.splitSdkVersion);
@@ -300,9 +314,11 @@ protected SplitFactoryImpl(String apiToken, SplitClientConfig config, CustomStor
_segmentCache = userCustomSegmentAdapterConsumer;
if (config.blockUntilReady() == -1) {
- //BlockUntilReady not been set
- _log.warn("no setBlockUntilReadyTimeout parameter has been set - incorrect control treatments could be logged” " +
- "if no ready config has been set when building factory");
+ // BlockUntilReady not been set
+ _log.warn(
+ "no setBlockUntilReadyTimeout parameter has been set - incorrect control treatments could be logged” "
+ +
+ "if no ready config has been set when building factory");
}
// SDKReadinessGates
@@ -313,7 +329,7 @@ protected SplitFactoryImpl(String apiToken, SplitClientConfig config, CustomStor
_impressionsSender = PluggableImpressionSender.create(customStorageWrapper);
_uniqueKeysTracker = createUniqueKeysTracker(config);
_impressionsManager = buildImpressionsManager(config, userCustomImpressionAdapterConsumer, userCustomImpressionAdapterProducer);
- _telemetrySyncTask = new TelemetrySyncTask(config.get_telemetryRefreshRate(), _telemetrySynchronizer, config.getThreadFactory());
+ _telemetrySyncTask = new TelemetrySyncTask(config.getTelemetryRefreshRate(), _telemetrySynchronizer, config.getThreadFactory());
SplitTasks splitTasks = SplitTasks.build(null, null,
_impressionsManager, null, _telemetrySyncTask, _uniqueKeysTracker);
@@ -321,9 +337,11 @@ protected SplitFactoryImpl(String apiToken, SplitClientConfig config, CustomStor
// Synchronizer
Synchronizer synchronizer = new ConsumerSynchronizer(splitTasks);
FlagSetsFilter flagSetsFilter = new FlagSetsFilterImpl(new HashSet<>());
- if(!config.getSetsFilter().isEmpty()) {
- _log.warn("FlagSets filter is not applicable for Consumer modes where the SDK does not keep rollout data in sync. FlagSet " +
- "filter was discarded");
+ if (!config.getSetsFilter().isEmpty()) {
+ _log.warn(
+ "FlagSets filter is not applicable for Consumer modes where the SDK does not keep rollout data in sync. FlagSet "
+ +
+ "filter was discarded");
}
_client = new SplitClientImpl(this,
userCustomSplitAdapterConsumer,
@@ -332,11 +350,10 @@ protected SplitFactoryImpl(String apiToken, SplitClientConfig config, CustomStor
config,
_gates,
_evaluator,
- _telemetryStorageProducer, //TelemetryEvaluation instance
- _telemetryStorageProducer, //TelemetryConfiguration instance
+ _telemetryStorageProducer, // TelemetryEvaluation instance
+ _telemetryStorageProducer, // TelemetryConfiguration instance
flagSetsFilter);
-
// SyncManager
_syncManager = new ConsumerSyncManager(synchronizer);
_syncManager.start();
@@ -355,7 +372,7 @@ protected SplitFactoryImpl(SplitClientConfig config) {
_telemetrySynchronizer = null;
_telemetrySyncTask = null;
_eventsTask = null;
- _httpclient = null;
+ _splitHttpClient = null;
_impressionsSender = null;
_rootTarget = null;
_eventsRootTarget = null;
@@ -369,29 +386,31 @@ protected SplitFactoryImpl(SplitClientConfig config) {
_gates = new SDKReadinessGates();
_segmentCache = segmentCache;
- //SegmentFetcher
+ // SegmentFetcher
SegmentChangeFetcher segmentChangeFetcher = new LocalhostSegmentFetcherNoop();
- if(config.segmentDirectory() != null){
+ if (config.segmentDirectory() != null) {
segmentChangeFetcher = new LocalhostSegmentChangeFetcher(config.segmentDirectory());
}
- _segmentSynchronizationTaskImp = new SegmentSynchronizationTaskImp(segmentChangeFetcher,
+ _segmentSynchronizationTaskImp = new SegmentSynchronizationTaskImp(segmentChangeFetcher,
config.segmentsRefreshRate(),
config.numThreadsForSegmentFetch(),
segmentCache,
_telemetryStorageProducer,
_splitCache,
- config.getThreadFactory());
+ config.getThreadFactory());
// SplitFetcher
SplitChangeFetcher splitChangeFetcher = createSplitChangeFetcher(config);
SplitParser splitParser = new SplitParser();
- _splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCache, _telemetryStorageProducer, flagSetsFilter);
+ _splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCache, _telemetryStorageProducer,
+ flagSetsFilter);
// SplitSynchronizationTask
- _splitSynchronizationTask = new SplitSynchronizationTask(_splitFetcher, splitCache, config.featuresRefreshRate(), config.getThreadFactory());
+ _splitSynchronizationTask = new SplitSynchronizationTask(_splitFetcher, splitCache,
+ config.featuresRefreshRate(), config.getThreadFactory());
_impressionsManager = new ImpressionsManager.NoOpImpressionsManager();
@@ -411,12 +430,13 @@ protected SplitFactoryImpl(SplitClientConfig config) {
config,
_gates,
_evaluator,
- _telemetryStorageProducer, //TelemetryEvaluation instance
- _telemetryStorageProducer, //TelemetryConfiguration instance
+ _telemetryStorageProducer, // TelemetryEvaluation instance
+ _telemetryStorageProducer, // TelemetryConfiguration instance
flagSetsFilter);
// Synchronizer
- Synchronizer synchronizer = new LocalhostSynchronizer(splitTasks, _splitFetcher, config.localhostRefreshEnabled());
+ Synchronizer synchronizer = new LocalhostSynchronizer(splitTasks, _splitFetcher,
+ config.localhostRefreshEnabled());
// SplitManager
_manager = new SplitManagerImpl(splitCache, config, _gates, _telemetryStorageProducer);
@@ -454,9 +474,9 @@ public synchronized void destroy() {
_log.info("Shutdown called for split");
_syncManager.shutdown();
_log.info("Successful shutdown of syncManager");
- if(OperationMode.STANDALONE.equals(_operationMode)) {
+ if (OperationMode.STANDALONE.equals(_operationMode)) {
_telemetryStorageProducer.recordSessionLength(System.currentTimeMillis() - _startTime);
- } else if(OperationMode.CONSUMER.equals(_operationMode)) {
+ } else if (OperationMode.CONSUMER.equals(_operationMode)) {
_userStorageWrapper.disconnect();
}
} catch (IOException e) {
@@ -471,7 +491,9 @@ public boolean isDestroyed() {
return isTerminated;
}
- private static CloseableHttpClient buildHttpClient(String apiToken, SplitClientConfig config, SDKMetadata sdkMetadata) {
+ private static SplitHttpClient buildSplitHttpClient(String apiToken, SplitClientConfig config,
+ SDKMetadata sdkMetadata, RequestDecorator requestDecorator)
+ throws URISyntaxException {
SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder.create()
.setSslContext(SSLContexts.createSystemDefault())
.setTlsVersions(TLS.V_1_1, TLS.V_1_2)
@@ -495,8 +517,6 @@ private static CloseableHttpClient buildHttpClient(String apiToken, SplitClientC
HttpClientBuilder httpClientbuilder = HttpClients.custom()
.setConnectionManager(cm)
.setDefaultRequestConfig(requestConfig)
- .addRequestInterceptorLast(AuthorizationInterceptorFilter.instance(apiToken))
- .addRequestInterceptorLast(SdkMetadataInterceptorFilter.instance(sdkMetadata))
.addRequestInterceptorLast(new GzipEncoderRequestInterceptor())
.addResponseInterceptorLast((new GzipDecoderResponseInterceptor()));
@@ -505,10 +525,14 @@ private static CloseableHttpClient buildHttpClient(String apiToken, SplitClientC
httpClientbuilder = setupProxy(httpClientbuilder, config);
}
- return httpClientbuilder.build();
+ return SplitHttpClientImpl.create(httpClientbuilder.build(),
+ requestDecorator,
+ apiToken,
+ sdkMetadata);
}
- private static CloseableHttpClient buildSSEdHttpClient(String apiToken, SplitClientConfig config, SDKMetadata sdkMetadata) {
+ private static CloseableHttpClient buildSSEdHttpClient(String apiToken, SplitClientConfig config,
+ SDKMetadata sdkMetadata) {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(Timeout.ofMilliseconds(SSE_CONNECT_TIMEOUT))
.build();
@@ -550,17 +574,20 @@ private static HttpClientBuilder setupProxy(HttpClientBuilder httpClientbuilder,
_log.debug("Proxy setup using credentials");
BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
AuthScope siteScope = new AuthScope(config.proxy().getHostName(), config.proxy().getPort());
- Credentials siteCreds = new UsernamePasswordCredentials(config.proxyUsername(), config.proxyPassword().toCharArray());
+ Credentials siteCreds = new UsernamePasswordCredentials(config.proxyUsername(),
+ config.proxyPassword().toCharArray());
credsProvider.setCredentials(siteScope, siteCreds);
httpClientbuilder.setDefaultCredentialsProvider(credsProvider);
}
- return httpClientbuilder;
+ return httpClientbuilder;
}
- private SegmentSynchronizationTaskImp buildSegments(SplitClientConfig config, SegmentCacheProducer segmentCacheProducer,
- SplitCacheConsumer splitCacheConsumer) throws URISyntaxException {
- SegmentChangeFetcher segmentChangeFetcher = HttpSegmentChangeFetcher.create(_httpclient, _rootTarget, _telemetryStorageProducer);
+ private SegmentSynchronizationTaskImp buildSegments(SplitClientConfig config,
+ SegmentCacheProducer segmentCacheProducer,
+ SplitCacheConsumer splitCacheConsumer) throws URISyntaxException {
+ SegmentChangeFetcher segmentChangeFetcher = HttpSegmentChangeFetcher.create(_splitHttpClient, _rootTarget,
+ _telemetryStorageProducer);
return new SegmentSynchronizationTaskImp(segmentChangeFetcher,
config.segmentsRefreshRate(),
@@ -571,14 +598,17 @@ private SegmentSynchronizationTaskImp buildSegments(SplitClientConfig config, Se
config.getThreadFactory());
}
- private SplitFetcher buildSplitFetcher(SplitCacheProducer splitCacheProducer, SplitParser splitParser, FlagSetsFilter flagSetsFilter) throws
- URISyntaxException {
- SplitChangeFetcher splitChangeFetcher = HttpSplitChangeFetcher.create(_httpclient, _rootTarget, _telemetryStorageProducer);
- return new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheProducer, _telemetryStorageProducer,flagSetsFilter);
+ private SplitFetcher buildSplitFetcher(SplitCacheProducer splitCacheProducer, SplitParser splitParser,
+ FlagSetsFilter flagSetsFilter) throws URISyntaxException {
+ SplitChangeFetcher splitChangeFetcher = HttpSplitChangeFetcher.create(_splitHttpClient, _rootTarget,
+ _telemetryStorageProducer);
+ return new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheProducer, _telemetryStorageProducer,
+ flagSetsFilter);
}
- private ImpressionsManagerImpl buildImpressionsManager(SplitClientConfig config, ImpressionsStorageConsumer impressionsStorageConsumer,
- ImpressionsStorageProducer impressionsStorageProducer) throws URISyntaxException {
+ private ImpressionsManagerImpl buildImpressionsManager(SplitClientConfig config,
+ ImpressionsStorageConsumer impressionsStorageConsumer,
+ ImpressionsStorageProducer impressionsStorageProducer) throws URISyntaxException {
List impressionListeners = new ArrayList<>();
if (config.integrationsConfig() != null) {
config.integrationsConfig().getImpressionsListeners(IntegrationsConfig.Execution.ASYNC).stream()
@@ -591,13 +621,15 @@ private ImpressionsManagerImpl buildImpressionsManager(SplitClientConfig config,
}
ProcessImpressionStrategy processImpressionStrategy = null;
ImpressionCounter counter = null;
- ImpressionListener listener = !impressionListeners.isEmpty() ? new ImpressionListener.FederatedImpressionListener(impressionListeners)
+ ImpressionListener listener = !impressionListeners.isEmpty()
+ ? new ImpressionListener.FederatedImpressionListener(impressionListeners)
: null;
- switch (config.impressionsMode()){
+ switch (config.impressionsMode()) {
case OPTIMIZED:
counter = new ImpressionCounter();
ImpressionObserver impressionObserver = new ImpressionObserver(config.getLastSeenCacheSize());
- processImpressionStrategy = new ProcessImpressionOptimized(listener != null, impressionObserver, counter, _telemetryStorageProducer);
+ processImpressionStrategy = new ProcessImpressionOptimized(listener != null, impressionObserver,
+ counter, _telemetryStorageProducer);
break;
case DEBUG:
impressionObserver = new ImpressionObserver(config.getLastSeenCacheSize());
@@ -608,7 +640,8 @@ private ImpressionsManagerImpl buildImpressionsManager(SplitClientConfig config,
processImpressionStrategy = new ProcessImpressionNone(listener != null, _uniqueKeysTracker, counter);
break;
}
- return ImpressionsManagerImpl.instance(config, _telemetryStorageProducer, impressionsStorageConsumer, impressionsStorageProducer,
+ return ImpressionsManagerImpl.instance(config, _telemetryStorageProducer, impressionsStorageConsumer,
+ impressionsStorageProducer,
_impressionsSender, processImpressionStrategy, counter, listener);
}
@@ -629,9 +662,10 @@ private SDKMetadata createSdkMetadata(boolean ipAddressEnabled, String splitSdkV
}
private void manageSdkReady(SplitClientConfig config) {
- ExecutorService executorService = buildExecutorService(config.getThreadFactory(), "SPLIT-SDKReadyForConsumer-%d");
+ ExecutorService executorService = buildExecutorService(config.getThreadFactory(),
+ "SPLIT-SDKReadyForConsumer-%d");
executorService.submit(() -> {
- while(!_userStorageWrapper.connect()) {
+ while (!_userStorageWrapper.connect()) {
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
@@ -640,16 +674,18 @@ private void manageSdkReady(SplitClientConfig config) {
}
}
_gates.sdkInternalReady();
- _telemetrySynchronizer.synchronizeConfig(config, System.currentTimeMillis(), ApiKeyCounter.getApiKeyCounterInstance().
- getFactoryInstances(), new ArrayList<>());
+ _telemetrySynchronizer.synchronizeConfig(config, System.currentTimeMillis(),
+ ApiKeyCounter.getApiKeyCounterInstance().getFactoryInstances(), new ArrayList<>());
});
}
- private UniqueKeysTracker createUniqueKeysTracker(SplitClientConfig config){
- if (config.impressionsMode().equals(ImpressionsManager.Mode.NONE)){
- int uniqueKeysRefreshRate = config.operationMode().equals(OperationMode.STANDALONE) ? config.uniqueKeysRefreshRateInMemory()
+ private UniqueKeysTracker createUniqueKeysTracker(SplitClientConfig config) {
+ if (config.impressionsMode().equals(ImpressionsManager.Mode.NONE)) {
+ int uniqueKeysRefreshRate = config.operationMode().equals(OperationMode.STANDALONE)
+ ? config.uniqueKeysRefreshRateInMemory()
: config.uniqueKeysRefreshRateRedis();
- return new UniqueKeysTrackerImp(_telemetrySynchronizer, uniqueKeysRefreshRate, config.filterUniqueKeysRefreshRate(),
+ return new UniqueKeysTrackerImp(_telemetrySynchronizer, uniqueKeysRefreshRate,
+ config.filterUniqueKeysRefreshRate(),
config.getThreadFactory());
}
return null;
@@ -670,7 +706,7 @@ private SplitChangeFetcher createSplitChangeFetcher(SplitClientConfig splitClien
inputStreamProvider = new StaticContentInputStreamProvider(inputStream);
}
try {
- switch (fileType){
+ switch (fileType) {
case JSON:
return new JsonLocalhostSplitChangeFetcher(inputStreamProvider);
case YAML:
@@ -682,10 +718,13 @@ private SplitChangeFetcher createSplitChangeFetcher(SplitClientConfig splitClien
}
} catch (Exception e) {
_log.warn(String.format("There was no file named %s found. " +
- "We created a split client that returns default treatments for all feature flags for all of your users. " +
- "If you wish to return a specific treatment for a feature flag, enter the name of that feature flag name and " +
- "treatment name separated by whitespace in %s; one pair per line. Empty lines or lines starting with '#' are " +
- "considered comments",
+ "We created a split client that returns default treatments for all feature flags for all of your users. "
+ +
+ "If you wish to return a specific treatment for a feature flag, enter the name of that feature flag name and "
+ +
+ "treatment name separated by whitespace in %s; one pair per line. Empty lines or lines starting with '#' are "
+ +
+ "considered comments",
splitFile, splitFile), e);
}
_log.warn(LEGACY_LOG_MESSAGE);
@@ -704,4 +743,4 @@ private FileTypeEnum getFileTypeFromFileName(String fileName) {
}
}
-}
\ No newline at end of file
+}
diff --git a/client/src/main/java/io/split/client/dtos/BetweenStringMatcherData.java b/client/src/main/java/io/split/client/dtos/BetweenStringMatcherData.java
new file mode 100644
index 000000000..3af1cc0c1
--- /dev/null
+++ b/client/src/main/java/io/split/client/dtos/BetweenStringMatcherData.java
@@ -0,0 +1,11 @@
+package io.split.client.dtos;
+
+/**
+ * Metadata to support the between matcher.
+ *
+ * @author adil
+ */
+public class BetweenStringMatcherData {
+ public String start;
+ public String end;
+}
diff --git a/client/src/main/java/io/split/client/dtos/Matcher.java b/client/src/main/java/io/split/client/dtos/Matcher.java
index 12c824d2c..fc2c65155 100644
--- a/client/src/main/java/io/split/client/dtos/Matcher.java
+++ b/client/src/main/java/io/split/client/dtos/Matcher.java
@@ -13,6 +13,7 @@ public class Matcher {
public WhitelistMatcherData whitelistMatcherData;
public UnaryNumericMatcherData unaryNumericMatcherData;
public BetweenMatcherData betweenMatcherData;
+ public BetweenStringMatcherData betweenStringMatcherData;
public DependencyMatcherData dependencyMatcherData;
public Boolean booleanMatcherData;
public String stringMatcherData;
diff --git a/client/src/main/java/io/split/client/dtos/MatcherType.java b/client/src/main/java/io/split/client/dtos/MatcherType.java
index ec22baec7..b8c78a7bd 100644
--- a/client/src/main/java/io/split/client/dtos/MatcherType.java
+++ b/client/src/main/java/io/split/client/dtos/MatcherType.java
@@ -30,5 +30,12 @@ public enum MatcherType {
EQUAL_TO_BOOLEAN,
/* Dependency Matcher */
- IN_SPLIT_TREATMENT
+ IN_SPLIT_TREATMENT,
+
+ /* Semver matchers */
+ EQUAL_TO_SEMVER,
+ GREATER_THAN_OR_EQUAL_TO_SEMVER,
+ LESS_THAN_OR_EQUAL_TO_SEMVER,
+ IN_LIST_SEMVER,
+ BETWEEN_SEMVER
}
diff --git a/client/src/main/java/io/split/client/dtos/RequestContext.java b/client/src/main/java/io/split/client/dtos/RequestContext.java
new file mode 100644
index 000000000..29fb7f77b
--- /dev/null
+++ b/client/src/main/java/io/split/client/dtos/RequestContext.java
@@ -0,0 +1,20 @@
+package io.split.client.dtos;
+
+import java.util.Map;
+import java.util.List;
+/**
+ * A structure returning a context for RequestDecorator class
+ */
+
+public class RequestContext
+{
+ private final Map> _headers;
+
+ public RequestContext(Map> headers) {
+ _headers = headers;
+ }
+
+ public Map> headers() {
+ return _headers;
+ }
+}
diff --git a/client/src/main/java/io/split/client/dtos/SplitHttpResponse.java b/client/src/main/java/io/split/client/dtos/SplitHttpResponse.java
new file mode 100644
index 000000000..a5474cf5b
--- /dev/null
+++ b/client/src/main/java/io/split/client/dtos/SplitHttpResponse.java
@@ -0,0 +1,34 @@
+package io.split.client.dtos;
+
+import java.util.Map;
+import org.apache.hc.core5.http.Header;
+/**
+ * A structure for returning http call results information
+ */
+public class SplitHttpResponse {
+ private final Integer _statusCode;
+ private final String _statusMessage;
+ private final String _body;
+ private final Header[] _responseHeaders;
+
+ public SplitHttpResponse(Integer statusCode, String statusMessage, String body, Header[] headers) {
+ _statusCode = statusCode;
+ _statusMessage = statusMessage;
+ _body = body;
+ _responseHeaders = headers;
+ }
+ public Integer statusCode() {
+ return _statusCode;
+ }
+ public String statusMessage() {
+ return _statusMessage;
+ }
+
+ public String body() {
+ return _body;
+ }
+
+ public Header[] responseHeaders() {
+ return _responseHeaders;
+ }
+}
diff --git a/client/src/main/java/io/split/client/events/EventsSender.java b/client/src/main/java/io/split/client/events/EventsSender.java
index 64ab3e90c..2c023ef3f 100644
--- a/client/src/main/java/io/split/client/events/EventsSender.java
+++ b/client/src/main/java/io/split/client/events/EventsSender.java
@@ -4,9 +4,9 @@
import io.split.client.dtos.Event;
import io.split.client.utils.Utils;
import io.split.service.HttpPostImp;
+import io.split.service.SplitHttpClient;
import io.split.telemetry.domain.enums.HttpParamsWrapper;
import io.split.telemetry.storage.TelemetryRuntimeProducer;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import java.net.URI;
import java.net.URISyntaxException;
@@ -18,20 +18,20 @@ public class EventsSender {
private static final String BULK_ENDPOINT_PATH = "api/events/bulk";
private final URI _bulkEndpoint;
- private final CloseableHttpClient _client;
+ private final SplitHttpClient _client;
private final TelemetryRuntimeProducer _telemetryRuntimeProducer;
private final HttpPostImp _httpPostImp;
- public static EventsSender create(CloseableHttpClient httpclient, URI eventsTarget, TelemetryRuntimeProducer telemetryRuntimeProducer)
+ public static EventsSender create(SplitHttpClient splitHttpclient, URI eventsTarget, TelemetryRuntimeProducer telemetryRuntimeProducer)
throws URISyntaxException {
- return new EventsSender(httpclient, Utils.appendPath(eventsTarget, BULK_ENDPOINT_PATH), telemetryRuntimeProducer);
+ return new EventsSender(splitHttpclient, Utils.appendPath(eventsTarget, BULK_ENDPOINT_PATH), telemetryRuntimeProducer);
}
- EventsSender(CloseableHttpClient httpclient, URI eventsTarget, TelemetryRuntimeProducer telemetryRuntimeProducer) {
- _client = checkNotNull(httpclient);
+ EventsSender(SplitHttpClient splitHttpclient, URI eventsTarget, TelemetryRuntimeProducer telemetryRuntimeProducer) {
+ _client = splitHttpclient;
_bulkEndpoint = checkNotNull(eventsTarget);
_telemetryRuntimeProducer = checkNotNull(telemetryRuntimeProducer);
- _httpPostImp = new HttpPostImp(httpclient, telemetryRuntimeProducer);
+ _httpPostImp = new HttpPostImp(_client, telemetryRuntimeProducer);
}
public void sendEvents(List _data) {
diff --git a/client/src/main/java/io/split/client/exceptions/SemverParseException.java b/client/src/main/java/io/split/client/exceptions/SemverParseException.java
new file mode 100644
index 000000000..892ba7535
--- /dev/null
+++ b/client/src/main/java/io/split/client/exceptions/SemverParseException.java
@@ -0,0 +1,7 @@
+package io.split.client.exceptions;
+
+public class SemverParseException extends Exception {
+ public SemverParseException(String message) {
+ super(message);
+ }
+}
diff --git a/client/src/main/java/io/split/client/impressions/HttpImpressionsSender.java b/client/src/main/java/io/split/client/impressions/HttpImpressionsSender.java
index 0a08e5dad..06df64cc4 100644
--- a/client/src/main/java/io/split/client/impressions/HttpImpressionsSender.java
+++ b/client/src/main/java/io/split/client/impressions/HttpImpressionsSender.java
@@ -2,16 +2,15 @@
import com.google.common.annotations.VisibleForTesting;
import io.split.client.dtos.ImpressionCount;
+import io.split.client.dtos.SplitHttpResponse;
import io.split.client.dtos.TestImpressions;
import io.split.client.utils.Utils;
+import io.split.service.SplitHttpClient;
import io.split.telemetry.domain.enums.HTTPLatenciesEnum;
import io.split.telemetry.domain.enums.LastSynchronizationRecordsEnum;
import io.split.telemetry.domain.enums.ResourceEnum;
import io.split.telemetry.storage.TelemetryRuntimeProducer;
-import org.apache.hc.client5.http.classic.methods.HttpPost;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpStatus;
import org.slf4j.Logger;
@@ -20,8 +19,10 @@
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -36,14 +37,15 @@ public class HttpImpressionsSender implements ImpressionsSender {
private static final Logger _logger = LoggerFactory.getLogger(HttpImpressionsSender.class);
- private final CloseableHttpClient _client;
+ private final SplitHttpClient _client;
private final URI _impressionBulkTarget;
private final URI _impressionCountTarget;
private final ImpressionsManager.Mode _mode;
private final TelemetryRuntimeProducer _telemetryRuntimeProducer;
- public static HttpImpressionsSender create(CloseableHttpClient client, URI eventsRootEndpoint, ImpressionsManager.Mode mode,
- TelemetryRuntimeProducer telemetryRuntimeProducer) throws URISyntaxException {
+ public static HttpImpressionsSender create(SplitHttpClient client, URI eventsRootEndpoint,
+ ImpressionsManager.Mode mode,
+ TelemetryRuntimeProducer telemetryRuntimeProducer) throws URISyntaxException {
return new HttpImpressionsSender(client,
Utils.appendPath(eventsRootEndpoint, BULK_ENDPOINT_PATH),
Utils.appendPath(eventsRootEndpoint, COUNT_ENDPOINT_PATH),
@@ -51,8 +53,9 @@ public static HttpImpressionsSender create(CloseableHttpClient client, URI event
telemetryRuntimeProducer);
}
- private HttpImpressionsSender(CloseableHttpClient client, URI impressionBulkTarget, URI impressionCountTarget, ImpressionsManager.Mode mode,
- TelemetryRuntimeProducer telemetryRuntimeProducer) {
+ private HttpImpressionsSender(SplitHttpClient client, URI impressionBulkTarget, URI impressionCountTarget,
+ ImpressionsManager.Mode mode,
+ TelemetryRuntimeProducer telemetryRuntimeProducer) {
_client = client;
_mode = mode;
_impressionBulkTarget = impressionBulkTarget;
@@ -62,33 +65,25 @@ private HttpImpressionsSender(CloseableHttpClient client, URI impressionBulkTarg
@Override
public void postImpressionsBulk(List impressions) {
-
- CloseableHttpResponse response = null;
long initTime = System.currentTimeMillis();
try {
HttpEntity entity = Utils.toJsonEntity(impressions);
+ Map> additionalHeaders = Collections.singletonMap(IMPRESSIONS_MODE_HEADER,
+ Collections.singletonList(_mode.toString()));
+ SplitHttpResponse response = _client.post(_impressionBulkTarget, entity, additionalHeaders);
- HttpPost request = new HttpPost(_impressionBulkTarget);
- request.addHeader(IMPRESSIONS_MODE_HEADER, _mode.toString());
- request.setEntity(entity);
-
- response = _client.execute(request);
-
- int status = response.getCode();
-
- if (status < HttpStatus.SC_OK || status >= HttpStatus.SC_MULTIPLE_CHOICES) {
- _telemetryRuntimeProducer.recordSyncError(ResourceEnum.IMPRESSION_SYNC, status);
- _logger.warn(String.format("Response status was: %s. Reason: %s", status , response.getReasonPhrase()));
+ if (response.statusCode() < HttpStatus.SC_OK || response.statusCode() >= HttpStatus.SC_MULTIPLE_CHOICES) {
+ _telemetryRuntimeProducer.recordSyncError(ResourceEnum.IMPRESSION_SYNC, response.statusCode());
}
- _telemetryRuntimeProducer.recordSuccessfulSync(LastSynchronizationRecordsEnum.IMPRESSIONS, System.currentTimeMillis());
+ _telemetryRuntimeProducer.recordSuccessfulSync(LastSynchronizationRecordsEnum.IMPRESSIONS,
+ System.currentTimeMillis());
} catch (Throwable t) {
_logger.warn("Exception when posting impressions" + impressions, t);
} finally {
- _telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.IMPRESSIONS, System.currentTimeMillis() - initTime);
- Utils.forceClose(response);
+ _telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.IMPRESSIONS,
+ System.currentTimeMillis() - initTime);
}
-
}
@Override
@@ -99,16 +94,18 @@ public void postCounters(HashMap raw) {
return;
}
- HttpPost request = new HttpPost(_impressionCountTarget);
- request.setEntity(Utils.toJsonEntity(ImpressionCount.fromImpressionCounterData(raw)));
- try (CloseableHttpResponse response = _client.execute(request)) {
- int status = response.getCode();
- if (status < HttpStatus.SC_OK || status >= HttpStatus.SC_MULTIPLE_CHOICES) {
- _telemetryRuntimeProducer.recordSyncError(ResourceEnum.IMPRESSION_COUNT_SYNC, status);
- _logger.warn(String.format("Response status was: %s. Reason: %s", status , response.getReasonPhrase()));
+ try {
+ SplitHttpResponse response = _client.post(_impressionCountTarget,
+ Utils.toJsonEntity(ImpressionCount.fromImpressionCounterData(raw)),
+ null);
+
+ if (response.statusCode() < HttpStatus.SC_OK || response.statusCode() >= HttpStatus.SC_MULTIPLE_CHOICES) {
+ _telemetryRuntimeProducer.recordSyncError(ResourceEnum.IMPRESSION_COUNT_SYNC, response.statusCode());
}
- _telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.IMPRESSIONS_COUNT, System.currentTimeMillis() - initTime);
- _telemetryRuntimeProducer.recordSuccessfulSync(LastSynchronizationRecordsEnum.IMPRESSIONS_COUNT, System.currentTimeMillis());
+ _telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.IMPRESSIONS_COUNT,
+ System.currentTimeMillis() - initTime);
+ _telemetryRuntimeProducer.recordSuccessfulSync(LastSynchronizationRecordsEnum.IMPRESSIONS_COUNT,
+ System.currentTimeMillis());
} catch (IOException exc) {
_logger.warn("Exception when posting impression counters: ", exc);
}
diff --git a/client/src/main/java/io/split/engine/SDKReadinessGates.java b/client/src/main/java/io/split/engine/SDKReadinessGates.java
index b4fd99fe6..c5ca5dbc8 100644
--- a/client/src/main/java/io/split/engine/SDKReadinessGates.java
+++ b/client/src/main/java/io/split/engine/SDKReadinessGates.java
@@ -3,9 +3,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
diff --git a/client/src/main/java/io/split/engine/common/PushManagerImp.java b/client/src/main/java/io/split/engine/common/PushManagerImp.java
index ff3343ed4..b6118efb6 100644
--- a/client/src/main/java/io/split/engine/common/PushManagerImp.java
+++ b/client/src/main/java/io/split/engine/common/PushManagerImp.java
@@ -83,7 +83,7 @@ public static PushManagerImp build(Synchronizer synchronizer,
PushStatusTracker pushStatusTracker = new PushStatusTrackerImp(statusMessages, telemetryRuntimeProducer);
return new PushManagerImp(new AuthApiClientImp(authUrl, splitAPI.getHttpClient(), telemetryRuntimeProducer),
EventSourceClientImp.build(streamingUrl, featureFlagsWorker, segmentWorker, pushStatusTracker, splitAPI.getSseHttpClient(),
- telemetryRuntimeProducer, threadFactory),
+ telemetryRuntimeProducer, threadFactory, splitAPI.getRequestDecorator()),
featureFlagsWorker,
segmentWorker,
pushStatusTracker,
diff --git a/client/src/main/java/io/split/engine/common/SplitAPI.java b/client/src/main/java/io/split/engine/common/SplitAPI.java
index 00b0c0e58..adb0500de 100644
--- a/client/src/main/java/io/split/engine/common/SplitAPI.java
+++ b/client/src/main/java/io/split/engine/common/SplitAPI.java
@@ -1,25 +1,30 @@
package io.split.engine.common;
+import io.split.client.RequestDecorator;
+import io.split.service.SplitHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SplitAPI {
- private final CloseableHttpClient _httpClient;
+ private final SplitHttpClient _httpClient;
private final CloseableHttpClient _sseHttpClient;
+ private final RequestDecorator _requestDecorator;
private static final Logger _log = LoggerFactory.getLogger(SplitAPI.class);
- private SplitAPI(CloseableHttpClient httpClient, CloseableHttpClient sseHttpClient) {
+ private SplitAPI(SplitHttpClient httpClient, CloseableHttpClient sseHttpClient, RequestDecorator requestDecorator) {
_httpClient = httpClient;
_sseHttpClient = sseHttpClient;
+ _requestDecorator = requestDecorator;
}
- public static SplitAPI build(CloseableHttpClient httpClient, CloseableHttpClient sseHttpClient){
- return new SplitAPI(httpClient,sseHttpClient);
+ public static SplitAPI build(SplitHttpClient httpClient, CloseableHttpClient sseHttpClient,
+ RequestDecorator requestDecorator) {
+ return new SplitAPI(httpClient, sseHttpClient, requestDecorator);
}
- public CloseableHttpClient getHttpClient() {
+ public SplitHttpClient getHttpClient() {
return _httpClient;
}
@@ -27,16 +32,20 @@ public CloseableHttpClient getSseHttpClient() {
return _sseHttpClient;
}
- public void close(){
+ public RequestDecorator getRequestDecorator() {
+ return _requestDecorator;
+ }
+
+ public void close() {
try {
_httpClient.close();
- } catch (Exception e){
- _log.error("Error trying to close httpcClient", e);
+ } catch (Exception e) {
+ _log.error("Error trying to close regular http client", e);
}
try {
_sseHttpClient.close();
- } catch (Exception e){
+ } catch (Exception e) {
_log.error("Error trying to close sseHttpClient", e);
}
}
-}
\ No newline at end of file
+}
diff --git a/client/src/main/java/io/split/engine/common/SplitTasks.java b/client/src/main/java/io/split/engine/common/SplitTasks.java
index 6045e826e..d2960d824 100644
--- a/client/src/main/java/io/split/engine/common/SplitTasks.java
+++ b/client/src/main/java/io/split/engine/common/SplitTasks.java
@@ -5,11 +5,8 @@
import io.split.client.impressions.UniqueKeysTracker;
import io.split.engine.experiments.SplitSynchronizationTask;
import io.split.engine.segments.SegmentSynchronizationTask;
-import io.split.engine.segments.SegmentSynchronizationTaskImp;
import io.split.telemetry.synchronizer.TelemetrySyncTask;
-import static com.google.common.base.Preconditions.checkNotNull;
-
public class SplitTasks {
private final SplitSynchronizationTask _splitSynchronizationTask;
private final SegmentSynchronizationTask _segmentSynchronizationTask;
diff --git a/client/src/main/java/io/split/engine/evaluator/Labels.java b/client/src/main/java/io/split/engine/evaluator/Labels.java
index b51a4e14b..ac5a465a9 100644
--- a/client/src/main/java/io/split/engine/evaluator/Labels.java
+++ b/client/src/main/java/io/split/engine/evaluator/Labels.java
@@ -6,4 +6,5 @@ public class Labels {
public static final String KILLED = "killed";
public static final String DEFINITION_NOT_FOUND = "definition not found";
public static final String EXCEPTION = "exception";
+ public static final String UNSUPPORTED_MATCHER = "targeting rule type unsupported by sdk";
}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/engine/experiments/SplitParser.java b/client/src/main/java/io/split/engine/experiments/SplitParser.java
index d5ae12f15..b320dff66 100644
--- a/client/src/main/java/io/split/engine/experiments/SplitParser.java
+++ b/client/src/main/java/io/split/engine/experiments/SplitParser.java
@@ -6,6 +6,9 @@
import io.split.client.dtos.MatcherGroup;
import io.split.client.dtos.Partition;
import io.split.client.dtos.Split;
+import io.split.client.dtos.ConditionType;
+import io.split.client.dtos.MatcherType;
+import io.split.engine.evaluator.Labels;
import io.split.engine.matchers.AllKeysMatcher;
import io.split.engine.matchers.AttributeMatcher;
import io.split.engine.matchers.BetweenMatcher;
@@ -16,6 +19,7 @@
import io.split.engine.matchers.GreaterThanOrEqualToMatcher;
import io.split.engine.matchers.LessThanOrEqualToMatcher;
import io.split.engine.matchers.UserDefinedSegmentMatcher;
+import io.split.engine.matchers.EqualToSemverMatcher;
import io.split.engine.matchers.collections.ContainsAllOfSetMatcher;
import io.split.engine.matchers.collections.ContainsAnyOfSetMatcher;
import io.split.engine.matchers.collections.EqualToSetMatcher;
@@ -25,6 +29,11 @@
import io.split.engine.matchers.strings.RegularExpressionMatcher;
import io.split.engine.matchers.strings.StartsWithAnyOfMatcher;
import io.split.engine.matchers.strings.WhitelistMatcher;
+import io.split.engine.matchers.GreaterThanOrEqualToSemverMatcher;
+import io.split.engine.matchers.LessThanOrEqualToSemverMatcher;
+import io.split.engine.matchers.InListSemverMatcher;
+import io.split.engine.matchers.BetweenSemverMatcher;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -59,6 +68,12 @@ private ParsedSplit parseWithoutExceptionHandling(Split split) {
for (Condition condition : split.conditions) {
List partitions = condition.partitions;
+ if (checkUnsupportedMatcherExist(condition.matcherGroup.matchers)) {
+ _log.error("Unsupported matcher type found for feature flag: " + split.name + " , will revert to default template matcher.");
+ parsedConditionList.clear();
+ parsedConditionList.add(getTemplateCondition());
+ break;
+ }
CombiningMatcher matcher = toMatcher(condition.matcherGroup);
parsedConditionList.add(new ParsedCondition(condition.conditionType, matcher, partitions, condition.label));
}
@@ -67,6 +82,34 @@ private ParsedSplit parseWithoutExceptionHandling(Split split) {
split.changeNumber, split.trafficAllocation, split.trafficAllocationSeed, split.algo, split.configurations, split.sets);
}
+ private boolean checkUnsupportedMatcherExist(List matchers) {
+ MatcherType typeCheck = null;
+ for (io.split.client.dtos.Matcher matcher : matchers) {
+ typeCheck = null;
+ try {
+ typeCheck = matcher.matcherType;
+ } catch (NullPointerException e) {
+ // If the exception is caught, it means unsupported matcher
+ break;
+ }
+ }
+ if (typeCheck != null) return false;
+ return true;
+ }
+
+ private ParsedCondition getTemplateCondition() {
+ List templatePartitions = Lists.newArrayList();
+ Partition partition = new Partition();
+ partition.treatment = "control";
+ partition.size = 100;
+ templatePartitions.add(partition);
+ return new ParsedCondition(
+ ConditionType.ROLLOUT,
+ CombiningMatcher.of(new AllKeysMatcher()),
+ templatePartitions,
+ Labels.UNSUPPORTED_MATCHER);
+ }
+
private CombiningMatcher toMatcher(MatcherGroup matcherGroup) {
List matchers = matcherGroup.matchers;
checkArgument(!matchers.isEmpty());
@@ -156,6 +199,26 @@ private AttributeMatcher toMatcher(Matcher matcher) {
+ ". matcher.booleanMatcherData() MUST NOT BE null");
delegate = new BooleanMatcher(matcher.booleanMatcherData);
break;
+ case EQUAL_TO_SEMVER:
+ checkNotNull(matcher.stringMatcherData, "stringMatcherData is required for EQUAL_TO_SEMVER matcher type");
+ delegate = new EqualToSemverMatcher(matcher.stringMatcherData);
+ break;
+ case GREATER_THAN_OR_EQUAL_TO_SEMVER:
+ checkNotNull(matcher.stringMatcherData, "stringMatcherData is required for GREATER_THAN_OR_EQUAL_TO_SEMVER matcher type");
+ delegate = new GreaterThanOrEqualToSemverMatcher(matcher.stringMatcherData);
+ break;
+ case LESS_THAN_OR_EQUAL_TO_SEMVER:
+ checkNotNull(matcher.stringMatcherData, "stringMatcherData is required for LESS_THAN_OR_EQUAL_SEMVER matcher type");
+ delegate = new LessThanOrEqualToSemverMatcher(matcher.stringMatcherData);
+ break;
+ case IN_LIST_SEMVER:
+ checkNotNull(matcher.whitelistMatcherData, "whitelistMatcherData is required for IN_LIST_SEMVER matcher type");
+ delegate = new InListSemverMatcher(matcher.whitelistMatcherData.whitelist);
+ break;
+ case BETWEEN_SEMVER:
+ checkNotNull(matcher.betweenStringMatcherData, "betweenStringMatcherData is required for BETWEEN_SEMVER matcher type");
+ delegate = new BetweenSemverMatcher(matcher.betweenStringMatcherData.start, matcher.betweenStringMatcherData.end);
+ break;
default:
throw new IllegalArgumentException("Unknown matcher type: " + matcher.matcherType);
}
diff --git a/client/src/main/java/io/split/engine/matchers/AllKeysMatcher.java b/client/src/main/java/io/split/engine/matchers/AllKeysMatcher.java
index 54de94ce0..790224ab1 100644
--- a/client/src/main/java/io/split/engine/matchers/AllKeysMatcher.java
+++ b/client/src/main/java/io/split/engine/matchers/AllKeysMatcher.java
@@ -1,7 +1,6 @@
package io.split.engine.matchers;
import io.split.engine.evaluator.EvaluationContext;
-import io.split.engine.evaluator.Evaluator;
import java.util.Map;
diff --git a/client/src/main/java/io/split/engine/matchers/AttributeMatcher.java b/client/src/main/java/io/split/engine/matchers/AttributeMatcher.java
index 487a5d3de..92deb0140 100644
--- a/client/src/main/java/io/split/engine/matchers/AttributeMatcher.java
+++ b/client/src/main/java/io/split/engine/matchers/AttributeMatcher.java
@@ -1,7 +1,6 @@
package io.split.engine.matchers;
import io.split.engine.evaluator.EvaluationContext;
-import io.split.engine.evaluator.Evaluator;
import java.util.Map;
import java.util.Objects;
diff --git a/client/src/main/java/io/split/engine/matchers/BetweenMatcher.java b/client/src/main/java/io/split/engine/matchers/BetweenMatcher.java
index 79ccd676c..a0ccfc1b7 100644
--- a/client/src/main/java/io/split/engine/matchers/BetweenMatcher.java
+++ b/client/src/main/java/io/split/engine/matchers/BetweenMatcher.java
@@ -2,7 +2,6 @@
import io.split.client.dtos.DataType;
import io.split.engine.evaluator.EvaluationContext;
-import io.split.engine.evaluator.Evaluator;
import java.util.Map;
diff --git a/client/src/main/java/io/split/engine/matchers/BetweenSemverMatcher.java b/client/src/main/java/io/split/engine/matchers/BetweenSemverMatcher.java
new file mode 100644
index 000000000..326e21830
--- /dev/null
+++ b/client/src/main/java/io/split/engine/matchers/BetweenSemverMatcher.java
@@ -0,0 +1,58 @@
+package io.split.engine.matchers;
+
+import io.split.engine.evaluator.EvaluationContext;
+
+import java.util.Map;
+
+public class BetweenSemverMatcher implements Matcher {
+
+ private final Semver _semverStart;
+ private final Semver _semverEnd;
+
+ public BetweenSemverMatcher(String semverStart, String semverEnd) {
+ _semverStart = Semver.build(semverStart);
+ _semverEnd = Semver.build(semverEnd);
+ }
+
+ @Override
+ public boolean match(Object matchValue, String bucketingKey, Map attributes, EvaluationContext evaluationContext) {
+ if (!(matchValue instanceof String) || _semverStart == null || _semverEnd == null) {
+ return false;
+ }
+ Semver matchSemver = Semver.build(matchValue.toString());
+ if (matchSemver == null) {
+ return false;
+ }
+
+ return matchSemver.compare(_semverStart) >= 0 && matchSemver.compare(_semverEnd) <= 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder bldr = new StringBuilder();
+ bldr.append("between semver ");
+ bldr.append(_semverStart.version());
+ bldr.append(" and ");
+ bldr.append(_semverEnd.version());
+ return bldr.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + _semverStart.hashCode() + _semverEnd.hashCode();
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) return false;
+ if (this == obj) return true;
+ if (!(obj instanceof BetweenSemverMatcher)) return false;
+
+ BetweenSemverMatcher other = (BetweenSemverMatcher) obj;
+
+ return _semverStart == other._semverStart && _semverEnd == other._semverEnd;
+ }
+
+}
diff --git a/client/src/main/java/io/split/engine/matchers/BooleanMatcher.java b/client/src/main/java/io/split/engine/matchers/BooleanMatcher.java
index 0a7418bb7..79d5a303f 100644
--- a/client/src/main/java/io/split/engine/matchers/BooleanMatcher.java
+++ b/client/src/main/java/io/split/engine/matchers/BooleanMatcher.java
@@ -1,7 +1,6 @@
package io.split.engine.matchers;
import io.split.engine.evaluator.EvaluationContext;
-import io.split.engine.evaluator.Evaluator;
import java.util.Map;
diff --git a/client/src/main/java/io/split/engine/matchers/CombiningMatcher.java b/client/src/main/java/io/split/engine/matchers/CombiningMatcher.java
index da75c53f3..4097ef851 100644
--- a/client/src/main/java/io/split/engine/matchers/CombiningMatcher.java
+++ b/client/src/main/java/io/split/engine/matchers/CombiningMatcher.java
@@ -2,10 +2,8 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
-import io.split.client.SplitClientImpl;
import io.split.client.dtos.MatcherCombiner;
import io.split.engine.evaluator.EvaluationContext;
-import io.split.engine.evaluator.Evaluator;
import java.util.List;
import java.util.Map;
diff --git a/client/src/main/java/io/split/engine/matchers/EqualToMatcher.java b/client/src/main/java/io/split/engine/matchers/EqualToMatcher.java
index dece1e539..9a1e32f37 100644
--- a/client/src/main/java/io/split/engine/matchers/EqualToMatcher.java
+++ b/client/src/main/java/io/split/engine/matchers/EqualToMatcher.java
@@ -2,7 +2,6 @@
import io.split.client.dtos.DataType;
import io.split.engine.evaluator.EvaluationContext;
-import io.split.engine.evaluator.Evaluator;
import java.util.Map;
diff --git a/client/src/main/java/io/split/engine/matchers/EqualToSemverMatcher.java b/client/src/main/java/io/split/engine/matchers/EqualToSemverMatcher.java
new file mode 100644
index 000000000..64d9135d2
--- /dev/null
+++ b/client/src/main/java/io/split/engine/matchers/EqualToSemverMatcher.java
@@ -0,0 +1,54 @@
+package io.split.engine.matchers;
+
+import io.split.engine.evaluator.EvaluationContext;
+
+import java.util.Map;
+
+public class EqualToSemverMatcher implements Matcher {
+
+ private final Semver _semVer;
+
+ public EqualToSemverMatcher(String semVer) {
+ _semVer = Semver.build(semVer);
+ }
+
+ @Override
+ public boolean match(Object matchValue, String bucketingKey, Map attributes, EvaluationContext evaluationContext) {
+ if (!(matchValue instanceof String) || _semVer == null) {
+ return false;
+ }
+ Semver matchSemver = Semver.build(matchValue.toString());
+ if (matchSemver == null) {
+ return false;
+ }
+
+ return matchSemver.version().equals(_semVer.version());
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder bldr = new StringBuilder();
+ bldr.append("== semver ");
+ bldr.append(_semVer.version());
+ return bldr.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + _semVer.hashCode();
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) return false;
+ if (this == obj) return true;
+ if (!(obj instanceof EqualToSemverMatcher)) return false;
+
+ EqualToSemverMatcher other = (EqualToSemverMatcher) obj;
+
+ return _semVer == other._semVer;
+ }
+
+}
diff --git a/client/src/main/java/io/split/engine/matchers/GreaterThanOrEqualToMatcher.java b/client/src/main/java/io/split/engine/matchers/GreaterThanOrEqualToMatcher.java
index 21620b920..1b83dc2c3 100644
--- a/client/src/main/java/io/split/engine/matchers/GreaterThanOrEqualToMatcher.java
+++ b/client/src/main/java/io/split/engine/matchers/GreaterThanOrEqualToMatcher.java
@@ -2,7 +2,6 @@
import io.split.client.dtos.DataType;
import io.split.engine.evaluator.EvaluationContext;
-import io.split.engine.evaluator.Evaluator;
import java.util.Map;
diff --git a/client/src/main/java/io/split/engine/matchers/GreaterThanOrEqualToSemverMatcher.java b/client/src/main/java/io/split/engine/matchers/GreaterThanOrEqualToSemverMatcher.java
new file mode 100644
index 000000000..ffc714cca
--- /dev/null
+++ b/client/src/main/java/io/split/engine/matchers/GreaterThanOrEqualToSemverMatcher.java
@@ -0,0 +1,54 @@
+package io.split.engine.matchers;
+
+import io.split.engine.evaluator.EvaluationContext;
+
+import java.util.Map;
+
+public class GreaterThanOrEqualToSemverMatcher implements Matcher {
+
+ private final Semver _semVer;
+
+ public GreaterThanOrEqualToSemverMatcher(String semVer) {
+ _semVer = Semver.build(semVer);
+ }
+
+ @Override
+ public boolean match(Object matchValue, String bucketingKey, Map attributes, EvaluationContext evaluationContext) {
+ if (!(matchValue instanceof String)|| _semVer == null) {
+ return false;
+ }
+ Semver matchSemver = Semver.build(matchValue.toString());
+ if (matchSemver == null) {
+ return false;
+ }
+
+ return matchSemver.compare(_semVer) >= 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder bldr = new StringBuilder();
+ bldr.append(">= semver ");
+ bldr.append(_semVer.version());
+ return bldr.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + _semVer.hashCode();
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) return false;
+ if (this == obj) return true;
+ if (!(obj instanceof GreaterThanOrEqualToSemverMatcher)) return false;
+
+ GreaterThanOrEqualToSemverMatcher other = (GreaterThanOrEqualToSemverMatcher) obj;
+
+ return _semVer == other._semVer;
+ }
+
+}
diff --git a/client/src/main/java/io/split/engine/matchers/InListSemverMatcher.java b/client/src/main/java/io/split/engine/matchers/InListSemverMatcher.java
new file mode 100644
index 000000000..69fd1ea45
--- /dev/null
+++ b/client/src/main/java/io/split/engine/matchers/InListSemverMatcher.java
@@ -0,0 +1,77 @@
+package io.split.engine.matchers;
+
+import io.split.engine.evaluator.EvaluationContext;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class InListSemverMatcher implements Matcher {
+
+ private final Set _semverlist = new HashSet<>();
+
+ public InListSemverMatcher(Collection whitelist) {
+ for (String item : whitelist) {
+ Semver semver = Semver.build(item);
+ if (semver == null) continue;
+
+ _semverlist.add(semver);
+ }
+ }
+
+ @Override
+ public boolean match(Object matchValue, String bucketingKey, Map attributes, EvaluationContext evaluationContext) {
+ if (!(matchValue instanceof String) || _semverlist.isEmpty()) {
+ return false;
+ }
+ Semver matchSemver = Semver.build(matchValue.toString());
+ if (matchSemver == null) {
+ return false;
+ }
+
+ for (Semver semverItem : _semverlist) {
+ if (semverItem.version().equals(matchSemver.version())) return true;
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder bldr = new StringBuilder();
+ bldr.append("in semver list [");
+ boolean first = true;
+
+ for (Semver item : _semverlist) {
+ if (!first) {
+ bldr.append(',');
+ }
+ bldr.append('"');
+ bldr.append(item.version());
+ bldr.append('"');
+ first = false;
+ }
+
+ bldr.append("]");
+ return bldr.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + _semverlist.hashCode();
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) return false;
+ if (this == obj) return true;
+ if (!(obj instanceof InListSemverMatcher)) return false;
+
+ InListSemverMatcher other = (InListSemverMatcher) obj;
+
+ return _semverlist == other._semverlist;
+ }
+
+}
diff --git a/client/src/main/java/io/split/engine/matchers/LessThanOrEqualToMatcher.java b/client/src/main/java/io/split/engine/matchers/LessThanOrEqualToMatcher.java
index bd4e779f2..24a74aaba 100644
--- a/client/src/main/java/io/split/engine/matchers/LessThanOrEqualToMatcher.java
+++ b/client/src/main/java/io/split/engine/matchers/LessThanOrEqualToMatcher.java
@@ -2,7 +2,6 @@
import io.split.client.dtos.DataType;
import io.split.engine.evaluator.EvaluationContext;
-import io.split.engine.evaluator.Evaluator;
import java.util.Map;
diff --git a/client/src/main/java/io/split/engine/matchers/LessThanOrEqualToSemverMatcher.java b/client/src/main/java/io/split/engine/matchers/LessThanOrEqualToSemverMatcher.java
new file mode 100644
index 000000000..dd05f8c4d
--- /dev/null
+++ b/client/src/main/java/io/split/engine/matchers/LessThanOrEqualToSemverMatcher.java
@@ -0,0 +1,54 @@
+package io.split.engine.matchers;
+
+import io.split.engine.evaluator.EvaluationContext;
+
+import java.util.Map;
+
+public class LessThanOrEqualToSemverMatcher implements Matcher {
+
+ private final Semver _semVer;
+
+ public LessThanOrEqualToSemverMatcher(String semVer) {
+ _semVer = Semver.build(semVer);
+ }
+
+ @Override
+ public boolean match(Object matchValue, String bucketingKey, Map attributes, EvaluationContext evaluationContext) {
+ if (!(matchValue instanceof String) || _semVer == null) {
+ return false;
+ }
+ Semver matchSemver = Semver.build(matchValue.toString());
+ if (matchSemver == null) {
+ return false;
+ }
+
+ return matchSemver.compare(_semVer) <= 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder bldr = new StringBuilder();
+ bldr.append("<= semver ");
+ bldr.append(_semVer.version());
+ return bldr.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + _semVer.hashCode();
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) return false;
+ if (this == obj) return true;
+ if (!(obj instanceof LessThanOrEqualToSemverMatcher)) return false;
+
+ LessThanOrEqualToSemverMatcher other = (LessThanOrEqualToSemverMatcher) obj;
+
+ return _semVer == other._semVer;
+ }
+
+}
diff --git a/client/src/main/java/io/split/engine/matchers/Semver.java b/client/src/main/java/io/split/engine/matchers/Semver.java
new file mode 100644
index 000000000..7a85a0d72
--- /dev/null
+++ b/client/src/main/java/io/split/engine/matchers/Semver.java
@@ -0,0 +1,176 @@
+package io.split.engine.matchers;
+
+import io.split.client.exceptions.SemverParseException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+
+public class Semver {
+ private static final String METADATA_DELIMITER = "+";
+ private static final String PRERELEASE_DELIMITER = "-";
+ private static final String VALUE_DELIMITER = "\\.";
+ private static final Logger _log = LoggerFactory.getLogger(Semver.class);
+
+ private Long _major;
+ private Long _minor;
+ private Long _patch;
+ private String[] _preRelease = new String[] {};
+ private boolean _isStable;
+ private String _metadata;
+ private String _version;
+
+ public static Semver build(String version) {
+ if (version.isEmpty()) return null;
+ try {
+ return new Semver(version);
+ } catch (Exception ex) {
+ _log.error("An error occurred during the creation of a Semver instance:", ex.getMessage());
+ return null;
+ }
+ }
+
+ public String version() {
+ return _version;
+ }
+
+ public Long major() {
+ return _major;
+ }
+
+ public Long minor() {
+ return _minor;
+ }
+
+ public Long patch() {
+ return _patch;
+ }
+
+ public String[] prerelease() {
+ return _preRelease;
+ }
+
+ public String metadata() {
+ return _metadata;
+ }
+
+ public boolean isStable() {
+ return _isStable;
+ }
+
+ /**
+ * Precedence comparision between 2 Semver objects.
+ *
+ * @return the value {@code 0} if {@code this == toCompare};
+ * a value less than {@code 0} if {@code this < toCompare}; and
+ * a value greater than {@code 0} if {@code this > toCompare}
+ */
+ public int compare(Semver toCompare) {
+ if (_version.equals(toCompare.version())) {
+ return 0;
+ }
+ // Compare major, minor, and patch versions numerically
+ int result = Long.compare(_major, toCompare.major());
+ if (result != 0) {
+ return result;
+ }
+ result = Long.compare(_minor, toCompare.minor());
+ if (result != 0) {
+ return result;
+ }
+ result = Long.compare(_patch, toCompare.patch());
+ if (result != 0) {
+ return result;
+ }
+ if (!_isStable && toCompare.isStable()) {
+ return -1;
+ } else if (_isStable && !toCompare.isStable()) {
+ return 1;
+ }
+ // Compare pre-release versions lexically
+ int minLength = Math.min(_preRelease.length, toCompare.prerelease().length);
+ for (int i = 0; i < minLength; i++) {
+ if (_preRelease[i].equals(toCompare.prerelease()[i])) {
+ continue;
+ }
+ if ( isNumeric(_preRelease[i]) && isNumeric(toCompare._preRelease[i])) {
+ return Long.compare(Integer.parseInt(_preRelease[i]), Long.parseLong(toCompare._preRelease[i]));
+ }
+ return adjustNumber(_preRelease[i].compareTo(toCompare._preRelease[i]));
+ }
+ // Compare lengths of pre-release versions
+ return Integer.compare(_preRelease.length, toCompare._preRelease.length);
+ }
+
+ private int adjustNumber(int number) {
+ if (number > 0) return 1;
+ if (number < 0) return -1;
+ return 0;
+ }
+ private Semver(String version) throws SemverParseException {
+ String vWithoutMetadata = setAndRemoveMetadataIfExists(version);
+ String vWithoutPreRelease = setAndRemovePreReleaseIfExists(vWithoutMetadata);
+ setMajorMinorAndPatch(vWithoutPreRelease);
+ _version = setVersion();
+ }
+ private String setAndRemoveMetadataIfExists(String version) throws SemverParseException {
+ int index = version.indexOf(METADATA_DELIMITER);
+ if (index == -1) {
+ return version;
+ }
+ _metadata = version.substring(index+1);
+ if (_metadata == null || _metadata.isEmpty()) {
+ throw new SemverParseException("Unable to convert to Semver, incorrect pre release data");
+ }
+ return version.substring(0, index);
+ }
+ private String setAndRemovePreReleaseIfExists(String vWithoutMetadata) throws SemverParseException {
+ int index = vWithoutMetadata.indexOf(PRERELEASE_DELIMITER);
+ if (index == -1) {
+ _isStable = true;
+ return vWithoutMetadata;
+ }
+ String preReleaseData = vWithoutMetadata.substring(index+1);
+ _preRelease = preReleaseData.split(VALUE_DELIMITER);
+ if (_preRelease == null || Arrays.stream(_preRelease).allMatch(pr -> pr == null || pr.isEmpty())) {
+ throw new SemverParseException("Unable to convert to Semver, incorrect pre release data");
+ }
+ return vWithoutMetadata.substring(0, index);
+ }
+ private void setMajorMinorAndPatch(String version) throws SemverParseException {
+ String[] vParts = version.split(VALUE_DELIMITER);
+ if (vParts.length != 3)
+ throw new SemverParseException("Unable to convert to Semver, incorrect format: " + version);
+ _major = Long.parseLong(vParts[0]);
+ _minor = Long.parseLong(vParts[1]);
+ _patch = Long.parseLong(vParts[2]);
+ }
+
+ private String setVersion() {
+ String toReturn = _major + VALUE_DELIMITER + _minor + VALUE_DELIMITER + _patch;
+ if (_preRelease != null && _preRelease.length != 0)
+ {
+ for (int i = 0; i < _preRelease.length; i++)
+ {
+ if (isNumeric(_preRelease[i]))
+ {
+ _preRelease[i] = Long.toString(Long.parseLong(_preRelease[i]));
+ }
+ }
+ toReturn = toReturn + PRERELEASE_DELIMITER + String.join(VALUE_DELIMITER, _preRelease);
+ }
+ if (_metadata != null && !_metadata.isEmpty()) {
+ toReturn = toReturn + METADATA_DELIMITER + _metadata;
+ }
+ return toReturn;
+ }
+
+ private static boolean isNumeric(String str) {
+ try {
+ Double.parseDouble(str);
+ return true;
+ } catch(NumberFormatException e){
+ return false;
+ }
+ }
+}
diff --git a/client/src/main/java/io/split/engine/matchers/collections/ContainsAllOfSetMatcher.java b/client/src/main/java/io/split/engine/matchers/collections/ContainsAllOfSetMatcher.java
index a2e477df7..5f4f9433a 100644
--- a/client/src/main/java/io/split/engine/matchers/collections/ContainsAllOfSetMatcher.java
+++ b/client/src/main/java/io/split/engine/matchers/collections/ContainsAllOfSetMatcher.java
@@ -1,7 +1,6 @@
package io.split.engine.matchers.collections;
import io.split.engine.evaluator.EvaluationContext;
-import io.split.engine.evaluator.Evaluator;
import io.split.engine.matchers.Matcher;
import java.util.Collection;
diff --git a/client/src/main/java/io/split/engine/matchers/collections/ContainsAnyOfSetMatcher.java b/client/src/main/java/io/split/engine/matchers/collections/ContainsAnyOfSetMatcher.java
index 93c7c2815..3a2514401 100644
--- a/client/src/main/java/io/split/engine/matchers/collections/ContainsAnyOfSetMatcher.java
+++ b/client/src/main/java/io/split/engine/matchers/collections/ContainsAnyOfSetMatcher.java
@@ -1,7 +1,6 @@
package io.split.engine.matchers.collections;
import io.split.engine.evaluator.EvaluationContext;
-import io.split.engine.evaluator.Evaluator;
import io.split.engine.matchers.Matcher;
import java.util.Collection;
diff --git a/client/src/main/java/io/split/engine/matchers/collections/EqualToSetMatcher.java b/client/src/main/java/io/split/engine/matchers/collections/EqualToSetMatcher.java
index bf811c70a..4a09c9efc 100644
--- a/client/src/main/java/io/split/engine/matchers/collections/EqualToSetMatcher.java
+++ b/client/src/main/java/io/split/engine/matchers/collections/EqualToSetMatcher.java
@@ -1,7 +1,6 @@
package io.split.engine.matchers.collections;
import io.split.engine.evaluator.EvaluationContext;
-import io.split.engine.evaluator.Evaluator;
import io.split.engine.matchers.Matcher;
import java.util.Collection;
diff --git a/client/src/main/java/io/split/engine/matchers/collections/PartOfSetMatcher.java b/client/src/main/java/io/split/engine/matchers/collections/PartOfSetMatcher.java
index 88974cb58..8bb5f1399 100644
--- a/client/src/main/java/io/split/engine/matchers/collections/PartOfSetMatcher.java
+++ b/client/src/main/java/io/split/engine/matchers/collections/PartOfSetMatcher.java
@@ -1,7 +1,6 @@
package io.split.engine.matchers.collections;
import io.split.engine.evaluator.EvaluationContext;
-import io.split.engine.evaluator.Evaluator;
import io.split.engine.matchers.Matcher;
import java.util.Collection;
diff --git a/client/src/main/java/io/split/engine/matchers/strings/ContainsAnyOfMatcher.java b/client/src/main/java/io/split/engine/matchers/strings/ContainsAnyOfMatcher.java
index 40f950cbe..b8cbe8fca 100644
--- a/client/src/main/java/io/split/engine/matchers/strings/ContainsAnyOfMatcher.java
+++ b/client/src/main/java/io/split/engine/matchers/strings/ContainsAnyOfMatcher.java
@@ -1,7 +1,6 @@
package io.split.engine.matchers.strings;
import io.split.engine.evaluator.EvaluationContext;
-import io.split.engine.evaluator.Evaluator;
import io.split.engine.matchers.Matcher;
import java.util.Collection;
diff --git a/client/src/main/java/io/split/engine/matchers/strings/EndsWithAnyOfMatcher.java b/client/src/main/java/io/split/engine/matchers/strings/EndsWithAnyOfMatcher.java
index 971fe01cc..32ac9f7f3 100644
--- a/client/src/main/java/io/split/engine/matchers/strings/EndsWithAnyOfMatcher.java
+++ b/client/src/main/java/io/split/engine/matchers/strings/EndsWithAnyOfMatcher.java
@@ -1,7 +1,6 @@
package io.split.engine.matchers.strings;
import io.split.engine.evaluator.EvaluationContext;
-import io.split.engine.evaluator.Evaluator;
import io.split.engine.matchers.Matcher;
import java.util.Collection;
diff --git a/client/src/main/java/io/split/engine/matchers/strings/RegularExpressionMatcher.java b/client/src/main/java/io/split/engine/matchers/strings/RegularExpressionMatcher.java
index f63dbcca8..f64b3264b 100644
--- a/client/src/main/java/io/split/engine/matchers/strings/RegularExpressionMatcher.java
+++ b/client/src/main/java/io/split/engine/matchers/strings/RegularExpressionMatcher.java
@@ -1,7 +1,6 @@
package io.split.engine.matchers.strings;
import io.split.engine.evaluator.EvaluationContext;
-import io.split.engine.evaluator.Evaluator;
import io.split.engine.matchers.Matcher;
import java.util.Map;
diff --git a/client/src/main/java/io/split/engine/matchers/strings/StartsWithAnyOfMatcher.java b/client/src/main/java/io/split/engine/matchers/strings/StartsWithAnyOfMatcher.java
index bb74ee185..7f1ed2cad 100644
--- a/client/src/main/java/io/split/engine/matchers/strings/StartsWithAnyOfMatcher.java
+++ b/client/src/main/java/io/split/engine/matchers/strings/StartsWithAnyOfMatcher.java
@@ -1,7 +1,6 @@
package io.split.engine.matchers.strings;
import io.split.engine.evaluator.EvaluationContext;
-import io.split.engine.evaluator.Evaluator;
import io.split.engine.matchers.Matcher;
import java.util.Collection;
diff --git a/client/src/main/java/io/split/engine/matchers/strings/WhitelistMatcher.java b/client/src/main/java/io/split/engine/matchers/strings/WhitelistMatcher.java
index d41123e53..5068c1437 100644
--- a/client/src/main/java/io/split/engine/matchers/strings/WhitelistMatcher.java
+++ b/client/src/main/java/io/split/engine/matchers/strings/WhitelistMatcher.java
@@ -1,7 +1,6 @@
package io.split.engine.matchers.strings;
import io.split.engine.evaluator.EvaluationContext;
-import io.split.engine.evaluator.Evaluator;
import io.split.engine.matchers.Matcher;
import java.util.Collection;
diff --git a/client/src/main/java/io/split/engine/splitter/Splitter.java b/client/src/main/java/io/split/engine/splitter/Splitter.java
index 60bb6d740..c867a81db 100644
--- a/client/src/main/java/io/split/engine/splitter/Splitter.java
+++ b/client/src/main/java/io/split/engine/splitter/Splitter.java
@@ -33,15 +33,15 @@ public static String getTreatment(String key, int seed, List partitio
static long hash(String key, int seed, int algo) {
switch (algo) {
case ALGO_MURMUR:
- return murmur_hash(key, seed);
+ return murmurHash(key, seed);
case ALGO_LEGACY:
default:
- return legacy_hash(key, seed);
+ return legacyHash(key, seed);
}
}
/*package private*/
- static long murmur_hash(String key, int seed) {
+ static long murmurHash(String key, int seed) {
return MurmurHash3.murmurhash3_x86_32(key, 0, key.length(), seed);
}
@@ -56,7 +56,7 @@ public static int getBucket(String key, int seed, int algo) {
}
/*package private*/
- static int legacy_hash(String key, int seed) {
+ static int legacyHash(String key, int seed) {
int h = 0;
for (int i = 0; i < key.length(); i++) {
h = 31 * h + key.charAt(i);
diff --git a/client/src/main/java/io/split/engine/sse/AuthApiClientImp.java b/client/src/main/java/io/split/engine/sse/AuthApiClientImp.java
index 9f750ada3..28464ebda 100644
--- a/client/src/main/java/io/split/engine/sse/AuthApiClientImp.java
+++ b/client/src/main/java/io/split/engine/sse/AuthApiClientImp.java
@@ -1,34 +1,34 @@
package io.split.engine.sse;
import com.google.gson.JsonObject;
+import io.split.client.dtos.SplitHttpResponse;
import io.split.client.utils.Json;
+import io.split.engine.common.FetchOptions;
import io.split.engine.sse.dtos.AuthenticationResponse;
import io.split.engine.sse.dtos.RawAuthResponse;
+import io.split.service.SplitHttpClient;
import io.split.telemetry.domain.enums.HTTPLatenciesEnum;
import io.split.telemetry.domain.enums.LastSynchronizationRecordsEnum;
import io.split.telemetry.storage.TelemetryRuntimeProducer;
-import org.apache.hc.client5.http.classic.methods.HttpGet;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.HttpStatus;
-import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.net.URIBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
-import java.nio.charset.StandardCharsets;
import static com.google.common.base.Preconditions.checkNotNull;
+import static io.split.Spec.SPEC_VERSION;
public class AuthApiClientImp implements AuthApiClient {
private static final Logger _log = LoggerFactory.getLogger(AuthApiClientImp.class);
- private final CloseableHttpClient _httpClient;
+ private static final String SPEC = "s";
+ private final SplitHttpClient _httpClient;
private final String _target;
private final TelemetryRuntimeProducer _telemetryRuntimeProducer;
- public AuthApiClientImp(String url, CloseableHttpClient httpClient, TelemetryRuntimeProducer telemetryRuntimeProducer) {
+ public AuthApiClientImp(String url, SplitHttpClient httpClient, TelemetryRuntimeProducer telemetryRuntimeProducer) {
_httpClient = checkNotNull(httpClient);
_target = checkNotNull(url);
_telemetryRuntimeProducer = checkNotNull(telemetryRuntimeProducer);
@@ -38,24 +38,17 @@ public AuthApiClientImp(String url, CloseableHttpClient httpClient, TelemetryRun
public AuthenticationResponse Authenticate() {
try {
long initTime = System.currentTimeMillis();
- URI uri = new URIBuilder(_target).build();
- HttpGet request = new HttpGet(uri);
-
- CloseableHttpResponse response = _httpClient.execute(request);
- Integer statusCode = response.getCode();
-
- if (_log.isDebugEnabled()) {
- _log.debug(String.format("[%s] %s. Status code: %s", request.getMethod(), uri.toURL(), statusCode));
- }
+ URI uri = new URIBuilder(_target).addParameter(SPEC, "" + SPEC_VERSION).build();
+ SplitHttpResponse response = _httpClient.get(uri, new FetchOptions.Builder().cacheControlHeaders(false).build(), null);
+ Integer statusCode = response.statusCode();
if (statusCode == HttpStatus.SC_OK) {
_log.debug(String.format("Success connection to: %s", _target));
- String jsonContent = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
_telemetryRuntimeProducer.recordTokenRefreshes();
_telemetryRuntimeProducer.recordSuccessfulSync(LastSynchronizationRecordsEnum.TOKEN, System.currentTimeMillis());
_telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.TOKEN, System.currentTimeMillis()-initTime);
- return getSuccessResponse(jsonContent);
+ return getSuccessResponse(response.body());
}
_log.error(String.format("Problem to connect to : %s. Response status: %s", _target, statusCode));
diff --git a/client/src/main/java/io/split/engine/sse/EventSourceClientImp.java b/client/src/main/java/io/split/engine/sse/EventSourceClientImp.java
index 35d1c05d7..212d929f3 100644
--- a/client/src/main/java/io/split/engine/sse/EventSourceClientImp.java
+++ b/client/src/main/java/io/split/engine/sse/EventSourceClientImp.java
@@ -2,6 +2,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
+import io.split.client.RequestDecorator;
import io.split.engine.sse.client.RawEvent;
import io.split.engine.sse.client.SSEClient;
import io.split.engine.sse.dtos.SegmentQueueDto;
@@ -40,7 +41,8 @@ public class EventSourceClientImp implements EventSourceClient {
PushStatusTracker pushStatusTracker,
CloseableHttpClient sseHttpClient,
TelemetryRuntimeProducer telemetryRuntimeProducer,
- ThreadFactory threadFactory) {
+ ThreadFactory threadFactory,
+ RequestDecorator requestDecorator) {
_baseStreamingUrl = checkNotNull(baseStreamingUrl);
_notificationParser = checkNotNull(notificationParser);
_notificationProcessor = checkNotNull(notificationProcessor);
@@ -51,7 +53,8 @@ public class EventSourceClientImp implements EventSourceClient {
status -> { _pushStatusTracker.handleSseStatus(status); return null; },
sseHttpClient,
telemetryRuntimeProducer,
- threadFactory);
+ threadFactory,
+ requestDecorator);
_firstEvent = new AtomicBoolean();
}
@@ -61,14 +64,16 @@ public static EventSourceClientImp build(String baseStreamingUrl,
PushStatusTracker pushStatusTracker,
CloseableHttpClient sseHttpClient,
TelemetryRuntimeProducer telemetryRuntimeProducer,
- ThreadFactory threadFactory) {
+ ThreadFactory threadFactory,
+ RequestDecorator requestDecorator) {
return new EventSourceClientImp(baseStreamingUrl,
new NotificationParserImp(),
NotificationProcessorImp.build(featureFlagsWorker, segmentWorker, pushStatusTracker),
pushStatusTracker,
sseHttpClient,
telemetryRuntimeProducer,
- threadFactory);
+ threadFactory,
+ requestDecorator);
}
@Override
diff --git a/client/src/main/java/io/split/engine/sse/client/SSEClient.java b/client/src/main/java/io/split/engine/sse/client/SSEClient.java
index 30dd16f20..9c2024d99 100644
--- a/client/src/main/java/io/split/engine/sse/client/SSEClient.java
+++ b/client/src/main/java/io/split/engine/sse/client/SSEClient.java
@@ -1,10 +1,12 @@
package io.split.engine.sse.client;
import com.google.common.base.Strings;
+import io.split.client.RequestDecorator;
import io.split.telemetry.domain.StreamingEvent;
import io.split.telemetry.domain.enums.StreamEventsEnum;
import io.split.telemetry.storage.TelemetryRuntimeProducer;
import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.slf4j.Logger;
@@ -56,6 +58,7 @@ private enum ConnectionState {
private final AtomicReference _ongoingResponse = new AtomicReference<>();
private final AtomicReference _ongoingRequest = new AtomicReference<>();
private AtomicBoolean _forcedStop;
+ private final RequestDecorator _requestDecorator;
private final TelemetryRuntimeProducer _telemetryRuntimeProducer;
@@ -63,13 +66,15 @@ public SSEClient(Function eventCallback,
Function statusCallback,
CloseableHttpClient client,
TelemetryRuntimeProducer telemetryRuntimeProducer,
- ThreadFactory threadFactory) {
+ ThreadFactory threadFactory,
+ RequestDecorator requestDecorator) {
_eventCallback = eventCallback;
_statusCallback = statusCallback;
_client = client;
_forcedStop = new AtomicBoolean();
_telemetryRuntimeProducer = checkNotNull(telemetryRuntimeProducer);
_connectionExecutor = buildExecutorService(threadFactory, "SPLIT-SSEConnection-%d");
+ _requestDecorator = requestDecorator;
}
public synchronized boolean open(URI uri) {
@@ -177,7 +182,9 @@ private void connectAndLoop(URI uri, CountDownLatch signal) {
}
private boolean establishConnection(URI uri, CountDownLatch signal) {
- _ongoingRequest.set(new HttpGet(uri));
+ HttpGet request = new HttpGet(uri);
+ request = (HttpGet) _requestDecorator.decorateHeaders(request);
+ _ongoingRequest.set(request);
try {
_ongoingResponse.set(_client.execute(_ongoingRequest.get()));
if (_ongoingResponse.get().getCode() != 200) {
diff --git a/client/src/main/java/io/split/engine/sse/dtos/RawAuthResponse.java b/client/src/main/java/io/split/engine/sse/dtos/RawAuthResponse.java
index 4082aac72..08f21d8a4 100644
--- a/client/src/main/java/io/split/engine/sse/dtos/RawAuthResponse.java
+++ b/client/src/main/java/io/split/engine/sse/dtos/RawAuthResponse.java
@@ -22,7 +22,7 @@ public RawAuthResponse(boolean pushEnabled, String token) {
this.pushEnabled = pushEnabled;
this.token = token;
- if (token != null && token != "") {
+ if (token != null && !token.isEmpty()) {
String tokenDecoded = decodeJwt();
this.jwt = Json.fromJson(tokenDecoded, Jwt.class);
} else {
diff --git a/client/src/main/java/io/split/engine/sse/dtos/RawMessageNotification.java b/client/src/main/java/io/split/engine/sse/dtos/RawMessageNotification.java
index f39bc8b20..9fc5ad6cd 100644
--- a/client/src/main/java/io/split/engine/sse/dtos/RawMessageNotification.java
+++ b/client/src/main/java/io/split/engine/sse/dtos/RawMessageNotification.java
@@ -1,7 +1,5 @@
package io.split.engine.sse.dtos;
-import java.util.Map;
-
public class RawMessageNotification {
private String id;
private String clientId;
diff --git a/client/src/main/java/io/split/service/HttpPostImp.java b/client/src/main/java/io/split/service/HttpPostImp.java
index a98744c7c..e5baa001b 100644
--- a/client/src/main/java/io/split/service/HttpPostImp.java
+++ b/client/src/main/java/io/split/service/HttpPostImp.java
@@ -1,15 +1,13 @@
package io.split.service;
+import io.split.client.dtos.SplitHttpResponse;
import io.split.client.utils.Utils;
import io.split.telemetry.domain.enums.HttpParamsWrapper;
import io.split.telemetry.storage.TelemetryRuntimeProducer;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.apache.hc.client5.http.classic.methods.HttpPost;
import java.net.URI;
@@ -17,10 +15,10 @@
public class HttpPostImp {
private static final Logger _logger = LoggerFactory.getLogger(HttpPostImp.class);
- private CloseableHttpClient _client;
+ private SplitHttpClient _client;
private final TelemetryRuntimeProducer _telemetryRuntimeProducer;
- public HttpPostImp(CloseableHttpClient client, TelemetryRuntimeProducer telemetryRuntimeProducer) {
+ public HttpPostImp(SplitHttpClient client, TelemetryRuntimeProducer telemetryRuntimeProducer) {
_client = client;
_telemetryRuntimeProducer = checkNotNull(telemetryRuntimeProducer);
}
@@ -28,19 +26,11 @@ public HttpPostImp(CloseableHttpClient client, TelemetryRuntimeProducer telemetr
public void post(URI uri, Object object, String posted, HttpParamsWrapper httpParamsWrapper) {
long initTime = System.currentTimeMillis();
HttpEntity entity = Utils.toJsonEntity(object);
- HttpPost request = new HttpPost(uri);
- request.setEntity(entity);
- if (_logger.isDebugEnabled()) {
- _logger.debug(String.format("[%s] %s", request.getMethod(), uri));
- }
-
- try (CloseableHttpResponse response = _client.execute(request)) {
-
- int status = response.getCode();
- if (status < HttpStatus.SC_OK || status >= HttpStatus.SC_MULTIPLE_CHOICES) {
- _telemetryRuntimeProducer.recordSyncError(httpParamsWrapper.getResourceEnum(), status);
- _logger.warn(String.format("Response status was: %s. Reason: %s", status , response.getReasonPhrase()));
+ try {
+ SplitHttpResponse response = _client.post(uri, entity, null);
+ if (response.statusCode() < HttpStatus.SC_OK || response.statusCode() >= HttpStatus.SC_MULTIPLE_CHOICES) {
+ _telemetryRuntimeProducer.recordSyncError(httpParamsWrapper.getResourceEnum(), response.statusCode());
return;
}
_telemetryRuntimeProducer.recordSyncLatency(httpParamsWrapper.getHttpLatenciesEnum(), System.currentTimeMillis() - initTime);
diff --git a/client/src/main/java/io/split/service/SplitHttpClient.java b/client/src/main/java/io/split/service/SplitHttpClient.java
new file mode 100644
index 000000000..1c88bcd4e
--- /dev/null
+++ b/client/src/main/java/io/split/service/SplitHttpClient.java
@@ -0,0 +1,35 @@
+package io.split.service;
+
+import io.split.engine.common.FetchOptions;
+import io.split.client.dtos.SplitHttpResponse;
+
+import org.apache.hc.core5.http.HttpEntity;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+
+public interface SplitHttpClient extends Closeable {
+ /**
+ * Wrapper for HTTP get method
+ *
+ * @param uri the URL to be used
+ * @param options The FetchOptions object that contains headers.
+ * @return The response structure SplitHttpResponse
+ */
+ public SplitHttpResponse get(URI uri, FetchOptions options, Map> additionalHeaders);
+
+ /**
+ * Wrapper for HTTP post method
+ *
+ * @param uri the URL to be used
+ * @param entity HttpEntity object that has The body load
+ * @param additionalHeaders Any additional headers to be added.
+ * @return The response structure SplitHttpResponse
+ */
+ public SplitHttpResponse post(URI uri,
+ HttpEntity entity,
+ Map> additionalHeaders) throws IOException;
+}
diff --git a/client/src/main/java/io/split/service/SplitHttpClientImpl.java b/client/src/main/java/io/split/service/SplitHttpClientImpl.java
new file mode 100644
index 000000000..64ca3a55c
--- /dev/null
+++ b/client/src/main/java/io/split/service/SplitHttpClientImpl.java
@@ -0,0 +1,148 @@
+package io.split.service;
+
+import io.split.client.RequestDecorator;
+import io.split.client.utils.SDKMetadata;
+import io.split.client.utils.Utils;
+import io.split.engine.common.FetchOptions;
+import io.split.client.dtos.SplitHttpResponse;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import org.apache.hc.core5.http.HttpRequest;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Map;
+
+public final class SplitHttpClientImpl implements SplitHttpClient {
+
+ private static final Logger _log = LoggerFactory.getLogger(SplitHttpClient.class);
+ private static final String HEADER_CACHE_CONTROL_NAME = "Cache-Control";
+ private static final String HEADER_CACHE_CONTROL_VALUE = "no-cache";
+ private static final String HEADER_API_KEY = "Authorization";
+ private static final String HEADER_CLIENT_KEY = "SplitSDKClientKey";
+ private static final String HEADER_CLIENT_MACHINE_NAME = "SplitSDKMachineName";
+ private static final String HEADER_CLIENT_MACHINE_IP = "SplitSDKMachineIP";
+ private static final String HEADER_CLIENT_VERSION = "SplitSDKVersion";
+
+ private final CloseableHttpClient _client;
+ private final RequestDecorator _requestDecorator;
+ private final String _apikey;
+ private final SDKMetadata _metadata;
+
+ public static SplitHttpClientImpl create(CloseableHttpClient client,
+ RequestDecorator requestDecorator,
+ String apikey,
+ SDKMetadata metadata) throws URISyntaxException {
+ return new SplitHttpClientImpl(client, requestDecorator, apikey, metadata);
+ }
+
+ private SplitHttpClientImpl(CloseableHttpClient client,
+ RequestDecorator requestDecorator,
+ String apikey,
+ SDKMetadata metadata) {
+ _client = client;
+ _requestDecorator = requestDecorator;
+ _apikey = apikey;
+ _metadata = metadata;
+ }
+
+ public SplitHttpResponse get(URI uri, FetchOptions options, Map> additionalHeaders) {
+ CloseableHttpResponse response = null;
+
+ try {
+ HttpGet request = new HttpGet(uri);
+ setBasicHeaders(request);
+ if (additionalHeaders != null) {
+ for (Map.Entry> entry : additionalHeaders.entrySet()) {
+ for (String value : entry.getValue()) {
+ request.addHeader(entry.getKey(), value);
+ }
+ }
+ }
+ if (options.cacheControlHeadersEnabled()) {
+ request.setHeader(HEADER_CACHE_CONTROL_NAME, HEADER_CACHE_CONTROL_VALUE);
+ }
+
+ _requestDecorator.decorateHeaders(request);
+
+ response = _client.execute(request);
+
+ if (_log.isDebugEnabled()) {
+ _log.debug(String.format("[%s] %s. Status code: %s", request.getMethod(), uri.toURL(),
+ response.getCode()));
+ }
+
+ String statusMessage = "";
+ if (response.getCode() < HttpStatus.SC_OK || response.getCode() >= HttpStatus.SC_MULTIPLE_CHOICES) {
+ _log.warn(String.format("Response status was: %s. Reason: %s", response.getCode(),
+ response.getReasonPhrase()));
+ statusMessage = response.getReasonPhrase();
+ }
+ return new SplitHttpResponse(response.getCode(),
+ statusMessage,
+ EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8),
+ response.getHeaders());
+ } catch (Exception e) {
+ throw new IllegalStateException(String.format("Problem in http get operation: %s", e), e);
+ } finally {
+ Utils.forceClose(response);
+ }
+ }
+
+ public SplitHttpResponse post(URI uri, HttpEntity entity, Map> additionalHeaders)
+ throws IOException {
+
+ CloseableHttpResponse response = null;
+ try {
+ HttpPost request = new HttpPost(uri);
+ setBasicHeaders(request);
+ if (additionalHeaders != null) {
+ for (Map.Entry> entry : additionalHeaders.entrySet()) {
+ for (String value : entry.getValue()) {
+ request.addHeader(entry.getKey(), value);
+ }
+ }
+ }
+ request.setEntity(entity);
+ request = (HttpPost) _requestDecorator.decorateHeaders(request);
+
+ response = _client.execute(request);
+
+ String statusMessage = "";
+ if (response.getCode() < HttpStatus.SC_OK || response.getCode() >= HttpStatus.SC_MULTIPLE_CHOICES) {
+ statusMessage = response.getReasonPhrase();
+ _log.warn(String.format("Response status was: %s. Reason: %s", response.getCode(),
+ response.getReasonPhrase()));
+ }
+ return new SplitHttpResponse(response.getCode(), statusMessage, "", response.getHeaders());
+ } catch (Exception e) {
+ throw new IOException(String.format("Problem in http post operation: %s", e), e);
+ } finally {
+ Utils.forceClose(response);
+ }
+ }
+
+ private void setBasicHeaders(HttpRequest request) {
+ request.setHeader(HEADER_API_KEY, "Bearer " + _apikey);
+ request.setHeader(HEADER_CLIENT_VERSION, _metadata.getSdkVersion());
+ request.setHeader(HEADER_CLIENT_MACHINE_IP, _metadata.getMachineIp());
+ request.setHeader(HEADER_CLIENT_MACHINE_NAME, _metadata.getMachineName());
+ request.setHeader(HEADER_CLIENT_KEY, _apikey.length() > 4
+ ? _apikey.substring(_apikey.length() - 4)
+ : _apikey);
+ }
+
+ @Override
+ public void close() throws IOException {
+ _client.close();
+ }
+}
diff --git a/client/src/main/java/io/split/storages/pluggable/domain/UserPipelineWrapper.java b/client/src/main/java/io/split/storages/pluggable/domain/UserPipelineWrapper.java
index 505fe11c6..81ddc691a 100644
--- a/client/src/main/java/io/split/storages/pluggable/domain/UserPipelineWrapper.java
+++ b/client/src/main/java/io/split/storages/pluggable/domain/UserPipelineWrapper.java
@@ -5,7 +5,6 @@
import pluggable.Pipeline;
import pluggable.Result;
-import java.util.ArrayList;
import java.util.List;
public class UserPipelineWrapper implements Pipeline{
diff --git a/client/src/main/java/io/split/telemetry/domain/HTTPErrors.java b/client/src/main/java/io/split/telemetry/domain/HTTPErrors.java
index dac746117..69c85ad85 100644
--- a/client/src/main/java/io/split/telemetry/domain/HTTPErrors.java
+++ b/client/src/main/java/io/split/telemetry/domain/HTTPErrors.java
@@ -39,59 +39,59 @@ public HTTPErrors() {
_telemetry = new ConcurrentHashMap<>();
}
- public Map get_splits() {
+ public Map getSplits() {
return _splits;
}
- public void set_splits(Map _splits) {
+ public void setSplits(Map _splits) {
this._splits = _splits;
}
- public Map get_segments() {
+ public Map getSegments() {
return _segments;
}
- public void set_segments(Map _segments) {
+ public void setSegments(Map _segments) {
this._segments = _segments;
}
- public Map get_impressions() {
+ public Map getImpressions() {
return _impressions;
}
- public void set_impressions(Map _impressions) {
+ public void setImpressions(Map _impressions) {
this._impressions = _impressions;
}
- public Map get_events() {
+ public Map getEvents() {
return _events;
}
- public void set_events(Map _events) {
+ public void setEvents(Map _events) {
this._events = _events;
}
- public Map get_token() {
+ public Map getToken() {
return _token;
}
- public void set_token(Map _token) {
+ public void setToken(Map _token) {
this._token = _token;
}
- public Map get_telemetry() {
+ public Map getTelemetry() {
return _telemetry;
}
- public void set_telemetry(Map _telemetry) {
+ public void setTelemetry(Map _telemetry) {
this._telemetry = _telemetry;
}
- public Map get_impressionsCount() {
+ public Map getImpressionsCount() {
return _impressionsCount;
}
- public void set_impressionsCount(Map _impressionsCount) {
+ public void setImpressionsCount(Map _impressionsCount) {
this._impressionsCount = _impressionsCount;
}
-}
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/telemetry/domain/HTTPLatencies.java b/client/src/main/java/io/split/telemetry/domain/HTTPLatencies.java
index 0e0791ed9..1b7ec2f8e 100644
--- a/client/src/main/java/io/split/telemetry/domain/HTTPLatencies.java
+++ b/client/src/main/java/io/split/telemetry/domain/HTTPLatencies.java
@@ -39,59 +39,59 @@ public HTTPLatencies() {
_telemetry = new ArrayList<>();
}
- public List get_splits() {
+ public List getSplits() {
return _splits;
}
- public void set_splits(List _splits) {
+ public void setSplits(List _splits) {
this._splits = _splits;
}
- public List get_segments() {
+ public List getSegments() {
return _segments;
}
- public void set_segments(List _segments) {
+ public void setSegments(List _segments) {
this._segments = _segments;
}
- public List get_impressions() {
+ public List getImpressions() {
return _impressions;
}
- public void set_impressions(List _impressions) {
+ public void setImpressions(List _impressions) {
this._impressions = _impressions;
}
- public List get_events() {
+ public List getEvents() {
return _events;
}
- public void set_events(List _events) {
+ public void setEvents(List _events) {
this._events = _events;
}
- public List get_token() {
+ public List getToken() {
return _token;
}
- public void set_token(List _token) {
+ public void setToken(List _token) {
this._token = _token;
}
- public List get_telemetry() {
+ public List getTelemetry() {
return _telemetry;
}
- public void set_telemetry(List _telemetry) {
+ public void setTelemetry(List _telemetry) {
this._telemetry = _telemetry;
}
- public List get_impressionsCount() {
+ public List getImpressionsCount() {
return _impressionsCount;
}
- public void set_impressionsCount(List _impressionsCount) {
+ public void setImpressionsCount(List _impressionsCount) {
this._impressionsCount = _impressionsCount;
}
-}
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/telemetry/domain/LastSynchronization.java b/client/src/main/java/io/split/telemetry/domain/LastSynchronization.java
index 59586562e..74889e1f9 100644
--- a/client/src/main/java/io/split/telemetry/domain/LastSynchronization.java
+++ b/client/src/main/java/io/split/telemetry/domain/LastSynchronization.java
@@ -26,59 +26,59 @@ public class LastSynchronization {
@SerializedName(FIELD_TELEMETRY)
private long _telemetry;
- public long get_splits() {
+ public long getSplits() {
return _splits;
}
- public void set_splits(long _splits) {
+ public void setSplits(long _splits) {
this._splits = _splits;
}
- public long get_segments() {
+ public long getSegments() {
return _segments;
}
- public void set_segments(long _segments) {
+ public void setSegments(long _segments) {
this._segments = _segments;
}
- public long get_impressions() {
+ public long getImpressions() {
return _impressions;
}
- public void set_impressions(long _impressions) {
+ public void setImpressions(long _impressions) {
this._impressions = _impressions;
}
- public long get_events() {
+ public long getEvents() {
return _events;
}
- public void set_events(long _events) {
+ public void setEvents(long _events) {
this._events = _events;
}
- public long get_token() {
+ public long getToken() {
return _token;
}
- public void set_token(long _token) {
+ public void setToken(long _token) {
this._token = _token;
}
- public long get_telemetry() {
+ public long getTelemetry() {
return _telemetry;
}
- public void set_telemetry(long _telemetry) {
+ public void setTelemetry(long _telemetry) {
this._telemetry = _telemetry;
}
- public long get_impressionsCount() {
+ public long getImpressionsCount() {
return _impressionsCount;
}
- public void set_impressionsCount(long _impressionsCount) {
+ public void setImpressionsCount(long _impressionsCount) {
this._impressionsCount = _impressionsCount;
}
-}
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/telemetry/domain/Rates.java b/client/src/main/java/io/split/telemetry/domain/Rates.java
index e80d26079..0ae5efdac 100644
--- a/client/src/main/java/io/split/telemetry/domain/Rates.java
+++ b/client/src/main/java/io/split/telemetry/domain/Rates.java
@@ -20,43 +20,43 @@ public class Rates {
@SerializedName(FIELD_TELEMETRY)
private long _telemetry;
- public long get_splits() {
+ public long getSplits() {
return _splits;
}
- public void set_splits(long _splits) {
+ public void setSplits(long _splits) {
this._splits = _splits;
}
- public long get_segments() {
+ public long getSegments() {
return _segments;
}
- public void set_segments(long _segments) {
+ public void setSegments(long _segments) {
this._segments = _segments;
}
- public long get_impressions() {
+ public long getImpressions() {
return _impressions;
}
- public void set_impressions(long _impressions) {
+ public void setImpressions(long _impressions) {
this._impressions = _impressions;
}
- public long get_events() {
+ public long getEvents() {
return _events;
}
- public void set_events(long _events) {
+ public void setEvents(long _events) {
this._events = _events;
}
- public long get_telemetry() {
+ public long getTelemetry() {
return _telemetry;
}
- public void set_telemetry(long _telemetry) {
+ public void setTelemetry(long _telemetry) {
this._telemetry = _telemetry;
}
-}
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/telemetry/domain/Stats.java b/client/src/main/java/io/split/telemetry/domain/Stats.java
index 7d271969f..55882cdc9 100644
--- a/client/src/main/java/io/split/telemetry/domain/Stats.java
+++ b/client/src/main/java/io/split/telemetry/domain/Stats.java
@@ -215,4 +215,4 @@ public UpdatesFromSSE getUpdatesFromSSE() {
public void setUpdatesFromSSE(UpdatesFromSSE updatesFromSSE) {
this._updatesFromSSE = updatesFromSSE;
}
-}
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/telemetry/domain/StreamingEvent.java b/client/src/main/java/io/split/telemetry/domain/StreamingEvent.java
index 161b29762..9fd3ae568 100644
--- a/client/src/main/java/io/split/telemetry/domain/StreamingEvent.java
+++ b/client/src/main/java/io/split/telemetry/domain/StreamingEvent.java
@@ -20,19 +20,19 @@ public StreamingEvent(int _type, long _data, long _timestamp) {
this._timestamp = _timestamp;
}
- public int get_type() {
+ public int getType() {
return _type;
}
- public void set_type(int _type) {
+ public void setType(int _type) {
this._type = _type;
}
- public long get_data() {
+ public long getData() {
return _data;
}
- public void set_data(long _data) {
+ public void setData(long _data) {
this._data = _data;
}
@@ -43,4 +43,4 @@ public long getTimestamp() {
public void setTimestamp(long timestamp) {
this._timestamp = timestamp;
}
-}
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/telemetry/domain/URLOverrides.java b/client/src/main/java/io/split/telemetry/domain/URLOverrides.java
index 5813f1d6c..587fdf3dd 100644
--- a/client/src/main/java/io/split/telemetry/domain/URLOverrides.java
+++ b/client/src/main/java/io/split/telemetry/domain/URLOverrides.java
@@ -20,43 +20,43 @@ public class URLOverrides {
@SerializedName(FIELD_TELEMETRY)
private boolean _telemetry;
- public boolean is_sdk() {
+ public boolean isSdk() {
return _sdk;
}
- public void set_sdk(boolean _sdk) {
+ public void setSdk(boolean _sdk) {
this._sdk = _sdk;
}
- public boolean is_events() {
+ public boolean isEvents() {
return _events;
}
- public void set_events(boolean _events) {
+ public void setEvents(boolean _events) {
this._events = _events;
}
- public boolean is_auth() {
+ public boolean isAuth() {
return _auth;
}
- public void set_auth(boolean _auth) {
+ public void setAuth(boolean _auth) {
this._auth = _auth;
}
- public boolean is_stream() {
+ public boolean isStream() {
return _stream;
}
- public void set_stream(boolean _stream) {
+ public void setStream(boolean _stream) {
this._stream = _stream;
}
- public boolean is_telemetry() {
+ public boolean isTelemetry() {
return _telemetry;
}
- public void set_telemetry(boolean _telemetry) {
+ public void setTelemetry(boolean _telemetry) {
this._telemetry = _telemetry;
}
-}
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/telemetry/storage/InMemoryTelemetryStorage.java b/client/src/main/java/io/split/telemetry/storage/InMemoryTelemetryStorage.java
index 94246fa7f..6958f110a 100644
--- a/client/src/main/java/io/split/telemetry/storage/InMemoryTelemetryStorage.java
+++ b/client/src/main/java/io/split/telemetry/storage/InMemoryTelemetryStorage.java
@@ -152,13 +152,13 @@ public long getEventStats(EventsDataRecordsEnum dataType) {
@Override
public LastSynchronization getLastSynchronization() {
LastSynchronization lastSynchronization = new LastSynchronization();
- lastSynchronization.set_splits(_lastSynchronizationRecords.get(LastSynchronizationRecordsEnum.SPLITS).get());
- lastSynchronization.set_segments(_lastSynchronizationRecords.get(LastSynchronizationRecordsEnum.SEGMENTS).get());
- lastSynchronization.set_impressions(_lastSynchronizationRecords.get(LastSynchronizationRecordsEnum.IMPRESSIONS).get());
- lastSynchronization.set_impressionsCount(_lastSynchronizationRecords.get(LastSynchronizationRecordsEnum.IMPRESSIONS_COUNT).get());
- lastSynchronization.set_events(_lastSynchronizationRecords.get(LastSynchronizationRecordsEnum.EVENTS).get());
- lastSynchronization.set_telemetry(_lastSynchronizationRecords.get(LastSynchronizationRecordsEnum.TELEMETRY).get());
- lastSynchronization.set_token(_lastSynchronizationRecords.get(LastSynchronizationRecordsEnum.TOKEN).get());
+ lastSynchronization.setSplits(_lastSynchronizationRecords.get(LastSynchronizationRecordsEnum.SPLITS).get());
+ lastSynchronization.setSegments(_lastSynchronizationRecords.get(LastSynchronizationRecordsEnum.SEGMENTS).get());
+ lastSynchronization.setImpressions(_lastSynchronizationRecords.get(LastSynchronizationRecordsEnum.IMPRESSIONS).get());
+ lastSynchronization.setImpressionsCount(_lastSynchronizationRecords.get(LastSynchronizationRecordsEnum.IMPRESSIONS_COUNT).get());
+ lastSynchronization.setEvents(_lastSynchronizationRecords.get(LastSynchronizationRecordsEnum.EVENTS).get());
+ lastSynchronization.setTelemetry(_lastSynchronizationRecords.get(LastSynchronizationRecordsEnum.TELEMETRY).get());
+ lastSynchronization.setToken(_lastSynchronizationRecords.get(LastSynchronizationRecordsEnum.TOKEN).get());
return lastSynchronization;
}
@@ -166,13 +166,13 @@ public LastSynchronization getLastSynchronization() {
@Override
public HTTPErrors popHTTPErrors() {
HTTPErrors errors = new HTTPErrors();
- errors.set_splits(_httpErrors.get(ResourceEnum.SPLIT_SYNC));
- errors.set_segments(_httpErrors.get(ResourceEnum.SEGMENT_SYNC));
- errors.set_impressions(_httpErrors.get(ResourceEnum.IMPRESSION_SYNC));
- errors.set_impressionsCount(_httpErrors.get(ResourceEnum.IMPRESSION_COUNT_SYNC));
- errors.set_events(_httpErrors.get(ResourceEnum.EVENT_SYNC));
- errors.set_telemetry(_httpErrors.get(ResourceEnum.TELEMETRY_SYNC));
- errors.set_token(_httpErrors.get(ResourceEnum.TOKEN_SYNC));
+ errors.setSplits(_httpErrors.get(ResourceEnum.SPLIT_SYNC));
+ errors.setSegments(_httpErrors.get(ResourceEnum.SEGMENT_SYNC));
+ errors.setImpressions(_httpErrors.get(ResourceEnum.IMPRESSION_SYNC));
+ errors.setImpressionsCount(_httpErrors.get(ResourceEnum.IMPRESSION_COUNT_SYNC));
+ errors.setEvents(_httpErrors.get(ResourceEnum.EVENT_SYNC));
+ errors.setTelemetry(_httpErrors.get(ResourceEnum.TELEMETRY_SYNC));
+ errors.setToken(_httpErrors.get(ResourceEnum.TOKEN_SYNC));
_httpErrors.clear();
initHttpErrors();
@@ -183,13 +183,13 @@ public HTTPErrors popHTTPErrors() {
@Override
public HTTPLatencies popHTTPLatencies(){
HTTPLatencies latencies = new HTTPLatencies();
- latencies.set_splits(_httpLatencies.get(HTTPLatenciesEnum.SPLITS).fetchAndClearAll());
- latencies.set_segments(_httpLatencies.get(HTTPLatenciesEnum.SEGMENTS).fetchAndClearAll());
- latencies.set_impressions(_httpLatencies.get(HTTPLatenciesEnum.IMPRESSIONS).fetchAndClearAll());
- latencies.set_impressionsCount(_httpLatencies.get(HTTPLatenciesEnum.IMPRESSIONS_COUNT).fetchAndClearAll());
- latencies.set_events(_httpLatencies.get(HTTPLatenciesEnum.EVENTS).fetchAndClearAll());
- latencies.set_telemetry(_httpLatencies.get(HTTPLatenciesEnum.TELEMETRY).fetchAndClearAll());
- latencies.set_token(_httpLatencies.get(HTTPLatenciesEnum.TOKEN).fetchAndClearAll());
+ latencies.setSplits(_httpLatencies.get(HTTPLatenciesEnum.SPLITS).fetchAndClearAll());
+ latencies.setSegments(_httpLatencies.get(HTTPLatenciesEnum.SEGMENTS).fetchAndClearAll());
+ latencies.setImpressions(_httpLatencies.get(HTTPLatenciesEnum.IMPRESSIONS).fetchAndClearAll());
+ latencies.setImpressionsCount(_httpLatencies.get(HTTPLatenciesEnum.IMPRESSIONS_COUNT).fetchAndClearAll());
+ latencies.setEvents(_httpLatencies.get(HTTPLatenciesEnum.EVENTS).fetchAndClearAll());
+ latencies.setTelemetry(_httpLatencies.get(HTTPLatenciesEnum.TELEMETRY).fetchAndClearAll());
+ latencies.setToken(_httpLatencies.get(HTTPLatenciesEnum.TOKEN).fetchAndClearAll());
return latencies;
}
diff --git a/client/src/main/java/io/split/telemetry/synchronizer/HttpTelemetryMemorySender.java b/client/src/main/java/io/split/telemetry/synchronizer/HttpTelemetryMemorySender.java
index 1cfd39fb3..4388eecaa 100644
--- a/client/src/main/java/io/split/telemetry/synchronizer/HttpTelemetryMemorySender.java
+++ b/client/src/main/java/io/split/telemetry/synchronizer/HttpTelemetryMemorySender.java
@@ -4,11 +4,11 @@
import io.split.client.dtos.UniqueKeys;
import io.split.client.utils.Utils;
import io.split.service.HttpPostImp;
+import io.split.service.SplitHttpClient;
import io.split.telemetry.domain.Config;
import io.split.telemetry.domain.Stats;
import io.split.telemetry.domain.enums.HttpParamsWrapper;
import io.split.telemetry.storage.TelemetryRuntimeProducer;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,7 +31,7 @@ public class HttpTelemetryMemorySender{
private final URI _uniqueKeysTarget;
private final HttpPostImp _httpPost;
- public static HttpTelemetryMemorySender create(CloseableHttpClient client, URI telemetryRootEndpoint,
+ public static HttpTelemetryMemorySender create(SplitHttpClient client, URI telemetryRootEndpoint,
TelemetryRuntimeProducer telemetryRuntimeProducer) throws URISyntaxException {
return new HttpTelemetryMemorySender(client,
Utils.appendPath(telemetryRootEndpoint,CONFIG_ENDPOINT_PATH),
@@ -42,7 +42,7 @@ public static HttpTelemetryMemorySender create(CloseableHttpClient client, URI t
}
@VisibleForTesting
- HttpTelemetryMemorySender(CloseableHttpClient client, URI impressionConfigTarget, URI impressionStatsTarget,
+ HttpTelemetryMemorySender(SplitHttpClient client, URI impressionConfigTarget, URI impressionStatsTarget,
URI uniqueKeysTarget,TelemetryRuntimeProducer telemetryRuntimeProducer) {
_httpPost = new HttpPostImp(client, telemetryRuntimeProducer);
_impressionConfigTarget = impressionConfigTarget;
diff --git a/client/src/main/java/io/split/telemetry/synchronizer/TelemetryInMemorySubmitter.java b/client/src/main/java/io/split/telemetry/synchronizer/TelemetryInMemorySubmitter.java
index 4f52bcdf1..5559332a1 100644
--- a/client/src/main/java/io/split/telemetry/synchronizer/TelemetryInMemorySubmitter.java
+++ b/client/src/main/java/io/split/telemetry/synchronizer/TelemetryInMemorySubmitter.java
@@ -7,6 +7,7 @@
import io.split.client.impressions.ImpressionsManager;
import io.split.integrations.IntegrationsConfig;
import io.split.integrations.NewRelicListener;
+import io.split.service.SplitHttpClient;
import io.split.storages.SegmentCacheConsumer;
import io.split.storages.SplitCacheConsumer;
import io.split.telemetry.domain.Config;
@@ -17,7 +18,6 @@
import io.split.telemetry.domain.enums.ImpressionsDataTypeEnum;
import io.split.telemetry.storage.TelemetryRuntimeProducer;
import io.split.telemetry.storage.TelemetryStorageConsumer;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import java.net.URI;
import java.net.URISyntaxException;
@@ -38,7 +38,7 @@ public class TelemetryInMemorySubmitter implements TelemetrySynchronizer{
private SegmentCacheConsumer _segmentCacheConsumer;
private final long _initStartTime;
- public TelemetryInMemorySubmitter(CloseableHttpClient client, URI telemetryRootEndpoint, TelemetryStorageConsumer telemetryStorageConsumer,
+ public TelemetryInMemorySubmitter(SplitHttpClient client, URI telemetryRootEndpoint, TelemetryStorageConsumer telemetryStorageConsumer,
SplitCacheConsumer splitCacheConsumer, SegmentCacheConsumer segmentCacheConsumer,
TelemetryRuntimeProducer telemetryRuntimeProducer, long initStartTime) throws URISyntaxException {
_httpHttpTelemetryMemorySender = HttpTelemetryMemorySender.create(client, telemetryRootEndpoint, telemetryRuntimeProducer);
@@ -109,17 +109,17 @@ Config generateConfig(SplitClientConfig splitClientConfig, long readyTimestamp,
}
List impressions = getImpressions(impressionsListeners);
- rates.set_telemetry(splitClientConfig.get_telemetryRefreshRate());
- rates.set_events(splitClientConfig.eventSendIntervalInMillis());
- rates.set_impressions(splitClientConfig.impressionsRefreshRate());
- rates.set_segments(splitClientConfig.segmentsRefreshRate());
- rates.set_splits(splitClientConfig.featuresRefreshRate());
+ rates.setTelemetry(splitClientConfig.getTelemetryRefreshRate());
+ rates.setEvents(splitClientConfig.eventSendIntervalInMillis());
+ rates.setImpressions(splitClientConfig.impressionsRefreshRate());
+ rates.setSegments(splitClientConfig.segmentsRefreshRate());
+ rates.setSplits(splitClientConfig.featuresRefreshRate());
- urlOverrides.set_auth(!SplitClientConfig.AUTH_ENDPOINT.equals(splitClientConfig.authServiceURL()));
- urlOverrides.set_stream(!SplitClientConfig.STREAMING_ENDPOINT.equals(splitClientConfig.streamingServiceURL()));
- urlOverrides.set_sdk(!SplitClientConfig.SDK_ENDPOINT.equals(splitClientConfig.endpoint()));
- urlOverrides.set_events(!SplitClientConfig.EVENTS_ENDPOINT.equals(splitClientConfig.eventsEndpoint()));
- urlOverrides.set_telemetry(!SplitClientConfig.TELEMETRY_ENDPOINT.equals(splitClientConfig.telemetryURL()));
+ urlOverrides.setAuth(!SplitClientConfig.AUTH_ENDPOINT.equals(splitClientConfig.authServiceURL()));
+ urlOverrides.setStream(!SplitClientConfig.STREAMING_ENDPOINT.equals(splitClientConfig.streamingServiceURL()));
+ urlOverrides.setSdk(!SplitClientConfig.SDK_ENDPOINT.equals(splitClientConfig.endpoint()));
+ urlOverrides.setEvents(!SplitClientConfig.EVENTS_ENDPOINT.equals(splitClientConfig.eventsEndpoint()));
+ urlOverrides.setTelemetry(!SplitClientConfig.TELEMETRY_ENDPOINT.equals(splitClientConfig.telemetryURL()));
config.setBurTimeouts(_telemetryStorageConsumer.getBURTimeouts());
config.setNonReadyUsages(_telemetryStorageConsumer.getNonReadyUsages());
@@ -170,4 +170,4 @@ private List getImpressions(List requestCaptor = ArgumentCaptor.forClass(ClassicHttpRequest.class);
CloseableHttpClient httpClientMock = Mockito.mock(CloseableHttpClient.class);
- when(httpClientMock.execute(requestCaptor.capture())).thenReturn(TestHelper.classicResponseToCloseableMock(response));
+ SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClientMock, new RequestDecorator(null),
+ "qwerty", metadata());
+
+ when(httpClientMock.execute(requestCaptor.capture()))
+ .thenReturn(TestHelper.classicResponseToCloseableMock(response));
Metrics.NoopMetrics metrics = new Metrics.NoopMetrics();
- HttpSegmentChangeFetcher fetcher = HttpSegmentChangeFetcher.create(httpClientMock, rootTarget, Mockito.mock(TelemetryStorage.class));
+ HttpSegmentChangeFetcher fetcher = HttpSegmentChangeFetcher.create(splitHtpClient, rootTarget,
+ Mockito.mock(TelemetryStorage.class));
fetcher.fetch("someSegment", -1, new FetchOptions.Builder().targetChangeNumber(123).build());
- fetcher.fetch("someSegment2",-1, new FetchOptions.Builder().build());
+ fetcher.fetch("someSegment2", -1, new FetchOptions.Builder().build());
List captured = requestCaptor.getAllValues();
Assert.assertEquals(captured.size(), 2);
Assert.assertTrue(captured.get(0).getUri().toString().contains("till=123"));
Assert.assertFalse(captured.get(1).getUri().toString().contains("till="));
}
+ @Test(expected = IllegalStateException.class)
+ public void testFetcherWithError() throws IOException, URISyntaxException, IllegalAccessException,
+ NoSuchMethodException, InvocationTargetException {
+ URI rootTarget = URI.create("https://api.split.io");
+
+ HttpEntity entityMock = Mockito.mock(HttpEntity.class);
+ when(entityMock.getContent()).thenReturn(new StringBufferInputStream("{\"till\": 1}"));
+ ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
+ when(response.getCode()).thenReturn(400);
+ when(response.getEntity()).thenReturn(entityMock);
+ when(response.getHeaders()).thenReturn(new Header[0]);
+
+ ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+ CloseableHttpClient httpClientMock = Mockito.mock(CloseableHttpClient.class);
+ SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClientMock, new RequestDecorator(null),
+ "qwerty", metadata());
+
+ when(httpClientMock.execute(requestCaptor.capture()))
+ .thenReturn(TestHelper.classicResponseToCloseableMock(response));
+
+ Metrics.NoopMetrics metrics = new Metrics.NoopMetrics();
+ HttpSegmentChangeFetcher fetcher = HttpSegmentChangeFetcher.create(splitHtpClient, rootTarget,
+ Mockito.mock(TelemetryStorage.class));
+
+ fetcher.fetch("someSegment", -1, new FetchOptions.Builder().build());
+ }
+
+ private SDKMetadata metadata() {
+ return new SDKMetadata("java-1.2.3", "1.2.3.4", "someIP");
+ }
}
diff --git a/client/src/test/java/io/split/client/HttpSplitChangeFetcherTest.java b/client/src/test/java/io/split/client/HttpSplitChangeFetcherTest.java
index 1784c44ed..8d18f8456 100644
--- a/client/src/test/java/io/split/client/HttpSplitChangeFetcherTest.java
+++ b/client/src/test/java/io/split/client/HttpSplitChangeFetcherTest.java
@@ -3,8 +3,11 @@
import io.split.TestHelper;
import io.split.client.dtos.Split;
import io.split.client.dtos.SplitChange;
+import io.split.client.utils.SDKMetadata;
import io.split.engine.common.FetchOptions;
import io.split.engine.metrics.Metrics;
+import io.split.service.SplitHttpClient;
+import io.split.service.SplitHttpClientImpl;
import io.split.telemetry.storage.InMemoryTelemetryStorage;
import io.split.telemetry.storage.TelemetryRuntimeProducer;
import io.split.telemetry.storage.TelemetryStorage;
@@ -26,21 +29,24 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.sql.Array;
+import java.util.*;
+import java.util.stream.Collectors;
import static org.mockito.Mockito.when;
public class HttpSplitChangeFetcherTest {
private static final TelemetryStorage TELEMETRY_STORAGE = Mockito.mock(InMemoryTelemetryStorage.class);
+
@Test
public void testDefaultURL() throws URISyntaxException {
URI rootTarget = URI.create("https://api.split.io");
CloseableHttpClient httpClient = HttpClients.custom().build();
Metrics.NoopMetrics metrics = new Metrics.NoopMetrics();
- HttpSplitChangeFetcher fetcher = HttpSplitChangeFetcher.create(httpClient, rootTarget, TELEMETRY_STORAGE);
+ SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClient, new RequestDecorator(null), "qwerty",
+ metadata());
+
+ HttpSplitChangeFetcher fetcher = HttpSplitChangeFetcher.create(splitHtpClient, rootTarget, TELEMETRY_STORAGE);
Assert.assertEquals("https://api.split.io/api/splitChanges", fetcher.getTarget().toString());
}
@@ -48,7 +54,10 @@ public void testDefaultURL() throws URISyntaxException {
public void testCustomURLNoPathNoBackslash() throws URISyntaxException {
URI rootTarget = URI.create("https://kubernetesturl.com/split");
CloseableHttpClient httpClient = HttpClients.custom().build();
- HttpSplitChangeFetcher fetcher = HttpSplitChangeFetcher.create(httpClient, rootTarget, TELEMETRY_STORAGE);
+ SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClient, new RequestDecorator(null), "qwerty",
+ metadata());
+
+ HttpSplitChangeFetcher fetcher = HttpSplitChangeFetcher.create(splitHtpClient, rootTarget, TELEMETRY_STORAGE);
Assert.assertEquals("https://kubernetesturl.com/split/api/splitChanges", fetcher.getTarget().toString());
}
@@ -56,7 +65,9 @@ public void testCustomURLNoPathNoBackslash() throws URISyntaxException {
public void testCustomURLAppendingPath() throws URISyntaxException {
URI rootTarget = URI.create("https://kubernetesturl.com/split/");
CloseableHttpClient httpClient = HttpClients.custom().build();
- HttpSplitChangeFetcher fetcher = HttpSplitChangeFetcher.create(httpClient, rootTarget, TELEMETRY_STORAGE);
+ SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClient, new RequestDecorator(null), "qwerty",
+ metadata());
+ HttpSplitChangeFetcher fetcher = HttpSplitChangeFetcher.create(splitHtpClient, rootTarget, TELEMETRY_STORAGE);
Assert.assertEquals("https://kubernetesturl.com/split/api/splitChanges", fetcher.getTarget().toString());
}
@@ -64,17 +75,24 @@ public void testCustomURLAppendingPath() throws URISyntaxException {
public void testCustomURLAppendingPathNoBackslash() throws URISyntaxException {
URI rootTarget = URI.create("https://kubernetesturl.com/split");
CloseableHttpClient httpClient = HttpClients.custom().build();
- HttpSplitChangeFetcher fetcher = HttpSplitChangeFetcher.create(httpClient, rootTarget, TELEMETRY_STORAGE);
+ SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClient, new RequestDecorator(null), "qwerty",
+ metadata());
+ HttpSplitChangeFetcher fetcher = HttpSplitChangeFetcher.create(splitHtpClient, rootTarget, TELEMETRY_STORAGE);
Assert.assertEquals("https://kubernetesturl.com/split/api/splitChanges", fetcher.getTarget().toString());
}
@Test
- public void testFetcherWithSpecialCharacters() throws URISyntaxException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, IOException {
+ public void testFetcherWithSpecialCharacters() throws URISyntaxException, InvocationTargetException,
+ NoSuchMethodException, IllegalAccessException, IOException {
URI rootTarget = URI.create("https://api.split.io");
- CloseableHttpClient httpClientMock = TestHelper.mockHttpClient("split-change-special-characters.json", HttpStatus.SC_OK);
+ CloseableHttpClient httpClientMock = TestHelper.mockHttpClient("split-change-special-characters.json",
+ HttpStatus.SC_OK);
+ SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClientMock, new RequestDecorator(null),
+ "qwerty",
+ metadata());
- HttpSplitChangeFetcher fetcher = HttpSplitChangeFetcher.create(httpClientMock, rootTarget, TELEMETRY_STORAGE);
+ HttpSplitChangeFetcher fetcher = HttpSplitChangeFetcher.create(splitHtpClient, rootTarget, TELEMETRY_STORAGE);
SplitChange change = fetcher.fetch(1234567, new FetchOptions.Builder().cacheControlHeaders(true).build());
@@ -91,11 +109,13 @@ public void testFetcherWithSpecialCharacters() throws URISyntaxException, Invoca
}
@Test
- public void testFetcherWithCDNBypassOption() throws IOException, URISyntaxException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
+ public void testFetcherWithCDNBypassOption() throws IOException, URISyntaxException, IllegalAccessException,
+ NoSuchMethodException, InvocationTargetException {
URI rootTarget = URI.create("https://api.split.io");
HttpEntity entityMock = Mockito.mock(HttpEntity.class);
- when(entityMock.getContent()).thenReturn(new ByteArrayInputStream("{\"till\": 1}".getBytes(StandardCharsets.UTF_8)));
+ when(entityMock.getContent())
+ .thenReturn(new ByteArrayInputStream("{\"till\": 1}".getBytes(StandardCharsets.UTF_8)));
ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
when(response.getCode()).thenReturn(200);
when(response.getEntity()).thenReturn(entityMock);
@@ -103,9 +123,13 @@ public void testFetcherWithCDNBypassOption() throws IOException, URISyntaxExcept
ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(ClassicHttpRequest.class);
CloseableHttpClient httpClientMock = Mockito.mock(CloseableHttpClient.class);
- when(httpClientMock.execute(requestCaptor.capture())).thenReturn(TestHelper.classicResponseToCloseableMock(response));
+ when(httpClientMock.execute(requestCaptor.capture()))
+ .thenReturn(TestHelper.classicResponseToCloseableMock(response));
+ SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClientMock, new RequestDecorator(null),
+ "qwerty", metadata());
- HttpSplitChangeFetcher fetcher = HttpSplitChangeFetcher.create(httpClientMock, rootTarget, Mockito.mock(TelemetryRuntimeProducer.class));
+ HttpSplitChangeFetcher fetcher = HttpSplitChangeFetcher.create(splitHtpClient, rootTarget,
+ Mockito.mock(TelemetryRuntimeProducer.class));
fetcher.fetch(-1, new FetchOptions.Builder().targetChangeNumber(123).build());
fetcher.fetch(-1, new FetchOptions.Builder().build());
@@ -119,10 +143,13 @@ public void testFetcherWithCDNBypassOption() throws IOException, URISyntaxExcept
public void testRandomNumberGeneration() throws URISyntaxException {
URI rootTarget = URI.create("https://api.split.io");
CloseableHttpClient httpClientMock = Mockito.mock(CloseableHttpClient.class);
- HttpSplitChangeFetcher fetcher = HttpSplitChangeFetcher.create(httpClientMock, rootTarget, Mockito.mock(TelemetryRuntimeProducer.class));
+ SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClientMock, new RequestDecorator(null),
+ "qwerty", metadata());
+ HttpSplitChangeFetcher fetcher = HttpSplitChangeFetcher.create(splitHtpClient, rootTarget,
+ Mockito.mock(TelemetryRuntimeProducer.class));
Set seen = new HashSet<>();
- long min = (long)Math.pow(2, 63) * (-1);
+ long min = (long) Math.pow(2, 63) * (-1);
final long total = 10000000;
for (long x = 0; x < total; x++) {
long r = fetcher.makeRandomTill();
@@ -132,4 +159,40 @@ public void testRandomNumberGeneration() throws URISyntaxException {
Assert.assertTrue(seen.size() >= (total * 0.9999));
}
-}
\ No newline at end of file
+
+ @Test(expected = IllegalStateException.class)
+ public void testURLTooLong() throws IOException, URISyntaxException, IllegalAccessException, NoSuchMethodException,
+ InvocationTargetException {
+ URI rootTarget = URI.create("https://api.split.io");
+
+ HttpEntity entityMock = Mockito.mock(HttpEntity.class);
+ when(entityMock.getContent())
+ .thenReturn(new ByteArrayInputStream("{\"till\": 1}".getBytes(StandardCharsets.UTF_8)));
+ ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
+ when(response.getCode()).thenReturn(414);
+ when(response.getEntity()).thenReturn(entityMock);
+ when(response.getHeaders()).thenReturn(new Header[0]);
+ CloseableHttpClient httpClientMock = Mockito.mock(CloseableHttpClient.class);
+ ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+ when(httpClientMock.execute(requestCaptor.capture()))
+ .thenReturn(TestHelper.classicResponseToCloseableMock(response));
+
+ SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClientMock, new RequestDecorator(null),
+ "qwerty", metadata());
+ HttpSplitChangeFetcher fetcher = HttpSplitChangeFetcher.create(splitHtpClient, rootTarget,
+ Mockito.mock(TelemetryRuntimeProducer.class));
+ List sets = new ArrayList();
+ for (Integer i = 0; i < 100; i++) {
+ sets.add("set" + i.toString());
+ }
+ String result = sets.stream()
+ .map(n -> String.valueOf(n))
+ .collect(Collectors.joining(",", "", ""));
+ fetcher.fetch(-1, new FetchOptions.Builder().flagSetsFilter(result).cacheControlHeaders(false).build());
+ }
+
+ private SDKMetadata metadata() {
+ return new SDKMetadata("java-1.2.3", "1.2.3.4", "someIP");
+ }
+
+}
diff --git a/client/src/test/java/io/split/client/RequestDecoratorTest.java b/client/src/test/java/io/split/client/RequestDecoratorTest.java
new file mode 100644
index 000000000..62868eb40
--- /dev/null
+++ b/client/src/test/java/io/split/client/RequestDecoratorTest.java
@@ -0,0 +1,111 @@
+package io.split.client;
+
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.ProtocolException;
+import org.junit.Assert;
+import org.junit.Test;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+import io.split.client.dtos.RequestContext;
+
+import java.util.List;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+public class RequestDecoratorTest {
+
+ @Test
+ public void testNoOp() {
+ RequestDecorator decorator = new RequestDecorator(null);
+ HttpGet request = new HttpGet("http://anyhost");
+ request = (HttpGet) decorator.decorateHeaders(request);
+ Assert.assertEquals(0, request.getHeaders().length);
+ request.addHeader("myheader", "value");
+ request = (HttpGet) decorator.decorateHeaders(request);
+ Assert.assertEquals(1, request.getHeaders().length);
+ }
+
+ @Test
+ public void testAddCustomHeaders() throws ProtocolException {
+ class MyCustomHeaders implements CustomHeaderDecorator {
+ public MyCustomHeaders() {}
+ @Override
+ public Map> getHeaderOverrides(RequestContext context) {
+ Map> additionalHeaders = context.headers();
+ additionalHeaders.put("first", Arrays.asList("1"));
+ additionalHeaders.put("second", Arrays.asList("2.1", "2.2"));
+ additionalHeaders.put("third", Arrays.asList("3"));
+ return additionalHeaders;
+ }
+ }
+ MyCustomHeaders myHeaders = new MyCustomHeaders();
+ RequestDecorator decorator = new RequestDecorator(myHeaders);
+ HttpGet request = new HttpGet("http://anyhost");
+ request.addHeader("first", "myfirstheader");
+ request = (HttpGet) decorator.decorateHeaders(request);
+
+ Assert.assertEquals(4, request.getHeaders().length);
+ Assert.assertEquals("1", request.getHeader("first").getValue());
+
+ Header[] second = request.getHeaders("second");
+ Assert.assertEquals("2.1", second[0].getValue());
+ Assert.assertEquals("2.2", second[1].getValue());
+ Assert.assertEquals("3", request.getHeader("third").getValue());
+
+ HttpPost request2 = new HttpPost("http://anyhost");
+ request2.addHeader("myheader", "value");
+ request2 = (HttpPost) decorator.decorateHeaders(request2);
+ Assert.assertEquals(5, request2.getHeaders().length);
+ }
+
+ @Test
+ public void testAddBlockedHeaders() throws ProtocolException {
+ class MyCustomHeaders implements CustomHeaderDecorator {
+ public MyCustomHeaders() {}
+ @Override
+ public Map> getHeaderOverrides(RequestContext context) {
+ Map> additionalHeaders = context.headers();
+ additionalHeaders.put("first", Arrays.asList("1"));
+ additionalHeaders.put("SplitSDKVersion", Arrays.asList("2.4"));
+ additionalHeaders.put("SplitMachineip", Arrays.asList("xx"));
+ additionalHeaders.put("splitMachineName", Arrays.asList("xx"));
+ additionalHeaders.put("splitimpressionsmode", Arrays.asList("xx"));
+ additionalHeaders.put("HOST", Arrays.asList("xx"));
+ additionalHeaders.put("referrer", Arrays.asList("xx"));
+ additionalHeaders.put("content-type", Arrays.asList("xx"));
+ additionalHeaders.put("content-length", Arrays.asList("xx"));
+ additionalHeaders.put("content-encoding", Arrays.asList("xx"));
+ additionalHeaders.put("ACCEPT", Arrays.asList("xx"));
+ additionalHeaders.put("keep-alive", Arrays.asList("xx"));
+ additionalHeaders.put("x-fastly-debug", Arrays.asList("xx"));
+ return additionalHeaders;
+ }
+ }
+ MyCustomHeaders myHeaders = new MyCustomHeaders();
+ RequestDecorator decorator = new RequestDecorator(myHeaders);
+ HttpGet request = new HttpGet("http://anyhost");
+ request = (HttpGet) decorator.decorateHeaders(request);
+ Assert.assertEquals(1, request.getHeaders().length);
+ Assert.assertEquals(null, request.getHeader("SplitSDKVersion"));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void customDecoratorError() {
+ class MyCustomHeaders implements CustomHeaderDecorator {
+ public MyCustomHeaders() {}
+ @Override
+ public Map> getHeaderOverrides(RequestContext context) {
+ throw new RuntimeException();
+ }
+ }
+ MyCustomHeaders myHeaders = new MyCustomHeaders();
+ RequestDecorator decorator = new RequestDecorator(myHeaders);
+ HttpGet request = new HttpGet("http://anyhost");
+ request = (HttpGet) decorator.decorateHeaders(request);
+ }
+}
\ No newline at end of file
diff --git a/client/src/test/java/io/split/client/SplitClientConfigTest.java b/client/src/test/java/io/split/client/SplitClientConfigTest.java
index 8e87ce938..1b640071c 100644
--- a/client/src/test/java/io/split/client/SplitClientConfigTest.java
+++ b/client/src/test/java/io/split/client/SplitClientConfigTest.java
@@ -4,12 +4,14 @@
import io.split.client.impressions.Impression;
import io.split.client.impressions.ImpressionListener;
import io.split.client.impressions.ImpressionsManager;
+import io.split.client.dtos.RequestContext;
import io.split.integrations.IntegrationsConfig;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -235,4 +237,21 @@ public void IntegrationConfigAsyncNotNull() {
Assert.assertEquals(0, config.integrationsConfig().getImpressionsListeners(IntegrationsConfig.Execution.SYNC).size());
Assert.assertEquals(1, config.integrationsConfig().getImpressionsListeners(IntegrationsConfig.Execution.ASYNC).size());
}
+
+ @Test
+ public void checkUserCustomdHeaderDecorator() {
+ CustomHeaderDecorator ucd = new CustomHeaderDecorator() {
+ @Override
+ public Map> getHeaderOverrides(RequestContext context) {
+ return null;
+ }
+ };
+ SplitClientConfig config = SplitClientConfig.builder().customHeaderDecorator(ucd).build();
+ Assert.assertNotNull(config.customHeaderDecorator());
+ Assert.assertEquals(ucd, config.customHeaderDecorator());
+
+ SplitClientConfig config2 = SplitClientConfig.builder().build();
+ Assert.assertNull(config2.customHeaderDecorator());
+
+ }
}
\ No newline at end of file
diff --git a/client/src/test/java/io/split/client/SplitManagerImplTest.java b/client/src/test/java/io/split/client/SplitManagerImplTest.java
index 1a67a5822..d4a5c6a87 100644
--- a/client/src/test/java/io/split/client/SplitManagerImplTest.java
+++ b/client/src/test/java/io/split/client/SplitManagerImplTest.java
@@ -164,7 +164,7 @@ public void splitNamesCallWithSplit() {
}
@Test
- public void block_until_ready_does_not_time_when_sdk_is_ready() throws TimeoutException, InterruptedException {
+ public void blockUntilReadyDoesNotTimeWhenSdkIsReady() throws TimeoutException, InterruptedException {
SDKReadinessGates ready = mock(SDKReadinessGates.class);
when(ready.waitUntilInternalReady(100)).thenReturn(true);
SplitManagerImpl splitManager = new SplitManagerImpl(mock(SplitCacheConsumer.class),
@@ -175,7 +175,7 @@ public void block_until_ready_does_not_time_when_sdk_is_ready() throws TimeoutEx
}
@Test(expected = TimeoutException.class)
- public void block_until_ready_times_when_sdk_is_not_ready() throws TimeoutException, InterruptedException {
+ public void blockUntilReadyTimesWhenSdkIsNotReady() throws TimeoutException, InterruptedException {
SDKReadinessGates ready = mock(SDKReadinessGates.class);
when(ready.waitUntilInternalReady(100)).thenReturn(false);
diff --git a/client/src/test/java/io/split/client/dtos/ImpressionCountTest.java b/client/src/test/java/io/split/client/dtos/ImpressionCountTest.java
index ee154c29b..140a4b30e 100644
--- a/client/src/test/java/io/split/client/dtos/ImpressionCountTest.java
+++ b/client/src/test/java/io/split/client/dtos/ImpressionCountTest.java
@@ -9,7 +9,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
diff --git a/client/src/test/java/io/split/client/events/EventsSenderTest.java b/client/src/test/java/io/split/client/events/EventsSenderTest.java
index 73ed904db..c9ddb754b 100644
--- a/client/src/test/java/io/split/client/events/EventsSenderTest.java
+++ b/client/src/test/java/io/split/client/events/EventsSenderTest.java
@@ -1,44 +1,79 @@
package io.split.client.events;
+import io.split.TestHelper;
+import io.split.client.RequestDecorator;
+import io.split.client.utils.SDKMetadata;
+import io.split.service.SplitHttpClient;
+import io.split.service.SplitHttpClientImpl;
import io.split.telemetry.storage.TelemetryRuntimeProducer;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.core5.http.HttpStatus;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
+import java.util.*;
public class EventsSenderTest {
- private static final TelemetryRuntimeProducer TELEMETRY_RUNTIME_CONSUMER = Mockito.mock(TelemetryRuntimeProducer.class);
+ private static final TelemetryRuntimeProducer TELEMETRY_RUNTIME_CONSUMER = Mockito
+ .mock(TelemetryRuntimeProducer.class);
private static final CloseableHttpClient CLOSEABLE_HTTP_CLIENT = Mockito.mock(CloseableHttpClient.class);
@Test
public void testDefaultURL() throws URISyntaxException {
+ SplitHttpClient SPLIT_HTTP_CLIENT = SplitHttpClientImpl.create(CLOSEABLE_HTTP_CLIENT,
+ new RequestDecorator(null), "qwerty", metadata());
URI rootTarget = URI.create("https://api.split.io");
- EventsSender fetcher = EventsSender.create(CLOSEABLE_HTTP_CLIENT, rootTarget, TELEMETRY_RUNTIME_CONSUMER);
+ EventsSender fetcher = EventsSender.create(SPLIT_HTTP_CLIENT, rootTarget, TELEMETRY_RUNTIME_CONSUMER);
Assert.assertEquals("https://api.split.io/api/events/bulk", fetcher.getBulkEndpoint().toString());
}
@Test
public void testCustomURLNoPathNoBackslash() throws URISyntaxException {
+ SplitHttpClient SPLIT_HTTP_CLIENT = SplitHttpClientImpl.create(CLOSEABLE_HTTP_CLIENT,
+ new RequestDecorator(null), "qwerty", metadata());
URI rootTarget = URI.create("https://kubernetesturl.com");
- EventsSender fetcher = EventsSender.create(CLOSEABLE_HTTP_CLIENT, rootTarget, TELEMETRY_RUNTIME_CONSUMER);
+ EventsSender fetcher = EventsSender.create(SPLIT_HTTP_CLIENT, rootTarget, TELEMETRY_RUNTIME_CONSUMER);
Assert.assertEquals("https://kubernetesturl.com/api/events/bulk", fetcher.getBulkEndpoint().toString());
}
@Test
public void testCustomURLAppendingPath() throws URISyntaxException {
+ SplitHttpClient SPLIT_HTTP_CLIENT = SplitHttpClientImpl.create(CLOSEABLE_HTTP_CLIENT,
+ new RequestDecorator(null), "qwerty", metadata());
URI rootTarget = URI.create("https://kubernetesturl.com/split/");
- EventsSender fetcher = EventsSender.create(CLOSEABLE_HTTP_CLIENT, rootTarget, TELEMETRY_RUNTIME_CONSUMER);
+ EventsSender fetcher = EventsSender.create(SPLIT_HTTP_CLIENT, rootTarget, TELEMETRY_RUNTIME_CONSUMER);
Assert.assertEquals("https://kubernetesturl.com/split/api/events/bulk", fetcher.getBulkEndpoint().toString());
}
@Test
public void testCustomURLAppendingPathNoBackslash() throws URISyntaxException {
+ SplitHttpClient SPLIT_HTTP_CLIENT = SplitHttpClientImpl.create(CLOSEABLE_HTTP_CLIENT,
+ new RequestDecorator(null), "qwerty", metadata());
URI rootTarget = URI.create("https://kubernetesturl.com/split");
- EventsSender fetcher = EventsSender.create(CLOSEABLE_HTTP_CLIENT, rootTarget, TELEMETRY_RUNTIME_CONSUMER);
+ EventsSender fetcher = EventsSender.create(SPLIT_HTTP_CLIENT, rootTarget, TELEMETRY_RUNTIME_CONSUMER);
Assert.assertEquals("https://kubernetesturl.com/split/api/events/bulk", fetcher.getBulkEndpoint().toString());
}
-}
\ No newline at end of file
+
+ @Test
+ public void testHttpError() throws URISyntaxException, IOException, InvocationTargetException,
+ IllegalAccessException, NoSuchMethodException {
+ URI rootTarget = URI.create("https://kubernetesturl.com/split");
+ CloseableHttpClient httpClient = TestHelper.mockHttpClient("", HttpStatus.SC_BAD_REQUEST);
+ SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClient, new RequestDecorator(null), "qwerty",
+ metadata());
+ EventsSender sender = EventsSender.create(splitHtpClient, rootTarget, TELEMETRY_RUNTIME_CONSUMER);
+ // should not raise exception
+ sender.sendEvents(new ArrayList<>());
+ }
+
+ private SDKMetadata metadata() {
+ return new SDKMetadata("java-1.2.3", "1.2.3.4", "someIP");
+ }
+
+}
diff --git a/client/src/test/java/io/split/client/impressions/HttpImpressionsSenderTest.java b/client/src/test/java/io/split/client/impressions/HttpImpressionsSenderTest.java
index ee586ffe4..18a4141cb 100644
--- a/client/src/test/java/io/split/client/impressions/HttpImpressionsSenderTest.java
+++ b/client/src/test/java/io/split/client/impressions/HttpImpressionsSenderTest.java
@@ -3,9 +3,13 @@
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import io.split.TestHelper;
+import io.split.client.RequestDecorator;
import io.split.client.dtos.ImpressionCount;
import io.split.client.dtos.KeyImpression;
import io.split.client.dtos.TestImpressions;
+import io.split.client.utils.SDKMetadata;
+import io.split.service.SplitHttpClient;
+import io.split.service.SplitHttpClientImpl;
import io.split.telemetry.storage.InMemoryTelemetryStorage;
import io.split.telemetry.storage.TelemetryStorage;
import org.apache.hc.client5.http.classic.methods.HttpPost;
@@ -24,6 +28,7 @@
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -42,43 +47,63 @@ public class HttpImpressionsSenderTest {
public void testDefaultURL() throws URISyntaxException {
URI rootTarget = URI.create("https://api.split.io");
CloseableHttpClient httpClient = HttpClients.custom().build();
- HttpImpressionsSender fetcher = HttpImpressionsSender.create(httpClient, rootTarget, ImpressionsManager.Mode.DEBUG, TELEMETRY_STORAGE);
- Assert.assertThat(fetcher.getTarget().toString(), Matchers.is(Matchers.equalTo("https://api.split.io/api/testImpressions/bulk")));
+ SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClient, new RequestDecorator(null), "qwerty",
+ metadata());
+ HttpImpressionsSender fetcher = HttpImpressionsSender.create(splitHtpClient, rootTarget,
+ ImpressionsManager.Mode.DEBUG, TELEMETRY_STORAGE);
+ Assert.assertThat(fetcher.getTarget().toString(),
+ Matchers.is(Matchers.equalTo("https://api.split.io/api/testImpressions/bulk")));
}
@Test
public void testCustomURLNoPathNoBackslash() throws URISyntaxException {
URI rootTarget = URI.create("https://kubernetesturl.com");
CloseableHttpClient httpClient = HttpClients.custom().build();
- HttpImpressionsSender fetcher = HttpImpressionsSender.create(httpClient, rootTarget, ImpressionsManager.Mode.DEBUG, TELEMETRY_STORAGE);
- Assert.assertThat(fetcher.getTarget().toString(), Matchers.is(Matchers.equalTo("https://kubernetesturl.com/api/testImpressions/bulk")));
+ SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClient, new RequestDecorator(null), "qwerty",
+ metadata());
+ HttpImpressionsSender fetcher = HttpImpressionsSender.create(splitHtpClient, rootTarget,
+ ImpressionsManager.Mode.DEBUG, TELEMETRY_STORAGE);
+ Assert.assertThat(fetcher.getTarget().toString(),
+ Matchers.is(Matchers.equalTo("https://kubernetesturl.com/api/testImpressions/bulk")));
}
@Test
public void testCustomURLAppendingPath() throws URISyntaxException {
URI rootTarget = URI.create("https://kubernetesturl.com/split/");
CloseableHttpClient httpClient = HttpClients.custom().build();
- HttpImpressionsSender fetcher = HttpImpressionsSender.create(httpClient, rootTarget, ImpressionsManager.Mode.DEBUG, TELEMETRY_STORAGE);
- Assert.assertThat(fetcher.getTarget().toString(), Matchers.is(Matchers.equalTo("https://kubernetesturl.com/split/api/testImpressions/bulk")));
+ SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClient, new RequestDecorator(null), "qwerty",
+ metadata());
+ HttpImpressionsSender fetcher = HttpImpressionsSender.create(splitHtpClient, rootTarget,
+ ImpressionsManager.Mode.DEBUG, TELEMETRY_STORAGE);
+ Assert.assertThat(fetcher.getTarget().toString(),
+ Matchers.is(Matchers.equalTo("https://kubernetesturl.com/split/api/testImpressions/bulk")));
}
@Test
public void testCustomURLAppendingPathNoBackslash() throws URISyntaxException {
URI rootTarget = URI.create("https://kubernetesturl.com/split");
CloseableHttpClient httpClient = HttpClients.custom().build();
- HttpImpressionsSender fetcher = HttpImpressionsSender.create(httpClient, rootTarget, ImpressionsManager.Mode.DEBUG, TELEMETRY_STORAGE);
- Assert.assertThat(fetcher.getTarget().toString(), Matchers.is(Matchers.equalTo("https://kubernetesturl.com/split/api/testImpressions/bulk")));
+ SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClient, new RequestDecorator(null), "qwerty",
+ metadata());
+ HttpImpressionsSender fetcher = HttpImpressionsSender.create(splitHtpClient, rootTarget,
+ ImpressionsManager.Mode.DEBUG, TELEMETRY_STORAGE);
+ Assert.assertThat(fetcher.getTarget().toString(),
+ Matchers.is(Matchers.equalTo("https://kubernetesturl.com/split/api/testImpressions/bulk")));
}
@Test
- public void testImpressionCountsEndpointOptimized() throws URISyntaxException, IOException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
+ public void testImpressionCountsEndpointOptimized() throws URISyntaxException, IOException, IllegalAccessException,
+ NoSuchMethodException, InvocationTargetException {
URI rootTarget = URI.create("https://kubernetesturl.com/split");
// Setup response mock
CloseableHttpClient httpClient = TestHelper.mockHttpClient("", HttpStatus.SC_OK);
+ SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClient, new RequestDecorator(null), "qwerty",
+ metadata());
// Send counters
- HttpImpressionsSender sender = HttpImpressionsSender.create(httpClient, rootTarget, ImpressionsManager.Mode.OPTIMIZED, TELEMETRY_STORAGE);
+ HttpImpressionsSender sender = HttpImpressionsSender.create(splitHtpClient, rootTarget,
+ ImpressionsManager.Mode.OPTIMIZED, TELEMETRY_STORAGE);
HashMap toSend = new HashMap<>();
toSend.put(new ImpressionCounter.Key("test1", 0), 4);
toSend.put(new ImpressionCounter.Key("test2", 0), 5);
@@ -88,8 +113,8 @@ public void testImpressionCountsEndpointOptimized() throws URISyntaxException, I
ArgumentCaptor captor = ArgumentCaptor.forClass(HttpUriRequest.class);
verify(httpClient).execute(captor.capture());
HttpUriRequest request = captor.getValue();
- assertThat(request.getUri(), is(equalTo(URI.create("https://kubernetesturl.com/split/api/testImpressions/count"))));
- assertThat(request.getHeaders().length, is(0));
+ assertThat(request.getUri(),
+ is(equalTo(URI.create("https://kubernetesturl.com/split/api/testImpressions/count"))));
assertThat(request, instanceOf(HttpPost.class));
HttpPost asPostRequest = (HttpPost) request;
InputStreamReader reader = new InputStreamReader(asPostRequest.getEntity().getContent());
@@ -101,14 +126,18 @@ public void testImpressionCountsEndpointOptimized() throws URISyntaxException, I
}
@Test
- public void testImpressionCountsEndpointDebug() throws URISyntaxException, IOException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
+ public void testImpressionCountsEndpointDebug() throws URISyntaxException, IOException, IllegalAccessException,
+ NoSuchMethodException, InvocationTargetException {
URI rootTarget = URI.create("https://kubernetesturl.com/split");
// Setup response mock
CloseableHttpClient httpClient = TestHelper.mockHttpClient("", HttpStatus.SC_OK);
+ SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClient, new RequestDecorator(null), "qwerty",
+ metadata());
// Send counters
- HttpImpressionsSender sender = HttpImpressionsSender.create(httpClient, rootTarget, ImpressionsManager.Mode.DEBUG, TELEMETRY_STORAGE);
+ HttpImpressionsSender sender = HttpImpressionsSender.create(splitHtpClient, rootTarget,
+ ImpressionsManager.Mode.DEBUG, TELEMETRY_STORAGE);
HashMap toSend = new HashMap<>();
toSend.put(new ImpressionCounter.Key("test1", 0), 4);
toSend.put(new ImpressionCounter.Key("test2", 0), 5);
@@ -119,49 +148,74 @@ public void testImpressionCountsEndpointDebug() throws URISyntaxException, IOExc
}
@Test
- public void testImpressionBulksEndpoint() throws URISyntaxException, IOException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
+ public void testImpressionBulksEndpoint() throws URISyntaxException, IOException, IllegalAccessException,
+ NoSuchMethodException, InvocationTargetException {
URI rootTarget = URI.create("https://kubernetesturl.com/split");
// Setup response mock
CloseableHttpClient httpClient = TestHelper.mockHttpClient("", HttpStatus.SC_OK);
+ SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClient, new RequestDecorator(null), "qwerty",
+ metadata());
- HttpImpressionsSender sender = HttpImpressionsSender.create(httpClient, rootTarget, ImpressionsManager.Mode.OPTIMIZED, TELEMETRY_STORAGE);
+ HttpImpressionsSender sender = HttpImpressionsSender.create(splitHtpClient, rootTarget,
+ ImpressionsManager.Mode.OPTIMIZED, TELEMETRY_STORAGE);
// Send impressions
List toSend = Arrays.asList(new TestImpressions("t1", Arrays.asList(
KeyImpression.fromImpression(new Impression("k1", null, "t1", "on", 123L, "r1", 456L, null)),
KeyImpression.fromImpression(new Impression("k2", null, "t1", "on", 123L, "r1", 456L, null)),
- KeyImpression.fromImpression(new Impression("k3", null, "t1", "on", 123L, "r1", 456L, null))
- )), new TestImpressions("t2", Arrays.asList(
- KeyImpression.fromImpression(new Impression("k1", null, "t2", "on", 123L, "r1", 456L, null)),
- KeyImpression.fromImpression(new Impression("k2", null, "t2", "on", 123L, "r1", 456L, null)),
- KeyImpression.fromImpression(new Impression("k3", null, "t2", "on", 123L, "r1", 456L, null))
- )));
+ KeyImpression.fromImpression(new Impression("k3", null, "t1", "on", 123L, "r1", 456L, null)))),
+ new TestImpressions("t2", Arrays.asList(
+ KeyImpression.fromImpression(new Impression("k1", null, "t2", "on", 123L, "r1", 456L, null)),
+ KeyImpression.fromImpression(new Impression("k2", null, "t2", "on", 123L, "r1", 456L, null)),
+ KeyImpression.fromImpression(new Impression("k3", null, "t2", "on", 123L, "r1", 456L, null)))));
sender.postImpressionsBulk(toSend);
// Capture outgoing request and validate it
ArgumentCaptor captor = ArgumentCaptor.forClass(HttpUriRequest.class);
verify(httpClient).execute(captor.capture());
HttpUriRequest request = captor.getValue();
- assertThat(request.getUri(), is(equalTo(URI.create("https://kubernetesturl.com/split/api/testImpressions/bulk"))));
- assertThat(request.getHeaders().length, is(1));
+ assertThat(request.getUri(),
+ is(equalTo(URI.create("https://kubernetesturl.com/split/api/testImpressions/bulk"))));
assertThat(request.getFirstHeader("SplitSDKImpressionsMode").getValue(), is(equalTo("OPTIMIZED")));
assertThat(request, instanceOf(HttpPost.class));
HttpPost asPostRequest = (HttpPost) request;
InputStreamReader reader = new InputStreamReader(asPostRequest.getEntity().getContent());
Gson gson = new Gson();
- List payload = gson.fromJson(reader, new TypeToken>() { }.getType());
+ List payload = gson.fromJson(reader, new TypeToken>() {
+ }.getType());
assertThat(payload.size(), is(equalTo(2)));
// Do the same flow for imrpessionsMode = debug
CloseableHttpClient httpClientDebugMode = TestHelper.mockHttpClient("", HttpStatus.SC_OK);
+ SplitHttpClient splitHtpClient2 = SplitHttpClientImpl.create(httpClientDebugMode, new RequestDecorator(null),
+ "qwerty", metadata());
- sender = HttpImpressionsSender.create(httpClientDebugMode, rootTarget, ImpressionsManager.Mode.DEBUG, TELEMETRY_STORAGE);
+ sender = HttpImpressionsSender.create(splitHtpClient2, rootTarget, ImpressionsManager.Mode.DEBUG,
+ TELEMETRY_STORAGE);
sender.postImpressionsBulk(toSend);
captor = ArgumentCaptor.forClass(HttpUriRequest.class);
verify(httpClientDebugMode).execute(captor.capture());
request = captor.getValue();
- assertThat(request.getHeaders().length, is(1));
assertThat(request.getFirstHeader("SplitSDKImpressionsMode").getValue(), is(equalTo("DEBUG")));
}
+
+ @Test
+ public void testHttpError() throws URISyntaxException, IOException, IllegalAccessException, NoSuchMethodException,
+ InvocationTargetException {
+ URI rootTarget = URI.create("https://kubernetesturl.com/split");
+ CloseableHttpClient httpClient = TestHelper.mockHttpClient("", HttpStatus.SC_BAD_REQUEST);
+ SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClient, new RequestDecorator(null), "qwerty",
+ metadata());
+ HttpImpressionsSender sender = HttpImpressionsSender.create(splitHtpClient, rootTarget,
+ ImpressionsManager.Mode.OPTIMIZED, TELEMETRY_STORAGE);
+ // Should not raise exception
+ sender.postImpressionsBulk(new ArrayList<>());
+ sender.postCounters(new HashMap<>());
+ }
+
+ private SDKMetadata metadata() {
+ return new SDKMetadata("java-1.2.3", "1.2.3.4", "someIP");
+ }
+
}
diff --git a/client/src/test/java/io/split/client/utils/CustomDispatcher.java b/client/src/test/java/io/split/client/utils/CustomDispatcher.java
index 62316b7a0..0b680156b 100644
--- a/client/src/test/java/io/split/client/utils/CustomDispatcher.java
+++ b/client/src/test/java/io/split/client/utils/CustomDispatcher.java
@@ -9,16 +9,16 @@
import java.util.*;
public class CustomDispatcher extends Dispatcher {
- public static final String INITIAL_SPLIT_CHANGES = "/api/splitChanges?since=-1";
- public static final String INITIAL_FLAGS_BY_SETS = "/api/splitChanges?since=-1&sets=set1%2Cset2";
- public static final String SINCE_1602796638344 = "/api/splitChanges?since=1602796638344&sets=set1%2Cset2";
- public static final String AUTH_ENABLED = "/api/auth/enabled";
- public static final String AUTH_DISABLED = "/api/auth/disabled";
- public static final String SINCE_1585948850109 = "/api/splitChanges?since=1585948850109";
- public static final String SINCE_1585948850109_FLAG_SET = "/api/splitChanges?since=-1&sets=set_1%2Cset_2";
- public static final String SINCE_1585948850110 = "/api/splitChanges?since=1585948850110";
- public static final String SINCE_1585948850111 = "/api/splitChanges?since=1585948850111";
- public static final String SINCE_1585948850112 = "/api/splitChanges?since=1585948850112";
+ public static final String INITIAL_SPLIT_CHANGES = "/api/splitChanges?s=1.1&since=-1";
+ public static final String INITIAL_FLAGS_BY_SETS = "/api/splitChanges?s=1.1&since=-1&sets=set1%2Cset2";
+ public static final String SINCE_1602796638344 = "/api/splitChanges?s=1.1&since=1602796638344&sets=set1%2Cset2";
+ public static final String AUTH_ENABLED = "/api/auth/enabled?s=1.1";
+ public static final String AUTH_DISABLED = "/api/auth/disabled?s=1.1";
+ public static final String SINCE_1585948850109 = "/api/splitChanges?s=1.1&since=1585948850109";
+ public static final String SINCE_1585948850109_FLAG_SET = "/api/splitChanges?s=1.1&since=-1&sets=set_1%2Cset_2";
+ public static final String SINCE_1585948850110 = "/api/splitChanges?s=1.1&since=1585948850110";
+ public static final String SINCE_1585948850111 = "/api/splitChanges?s=1.1&since=1585948850111";
+ public static final String SINCE_1585948850112 = "/api/splitChanges?s=1.1&since=1585948850112";
public static final String SEGMENT_TEST_INITIAL = "/api/segmentChanges/segment-test?since=-1";
public static final String SEGMENT3_INITIAL = "/api/segmentChanges/segment3?since=-1";
public static final String SEGMENT3_SINCE_1585948850110 = "/api/segmentChanges/segment3?since=1585948850110";
diff --git a/client/src/test/java/io/split/engine/experiments/SplitParserTest.java b/client/src/test/java/io/split/engine/experiments/SplitParserTest.java
index 634e93c62..e9c0e63a5 100644
--- a/client/src/test/java/io/split/engine/experiments/SplitParserTest.java
+++ b/client/src/test/java/io/split/engine/experiments/SplitParserTest.java
@@ -1,11 +1,21 @@
package io.split.engine.experiments;
import com.google.common.collect.Lists;
+import io.split.client.dtos.Condition;
+import io.split.client.dtos.DataType;
+import io.split.client.dtos.Matcher;
+import io.split.client.dtos.MatcherCombiner;
+import io.split.client.dtos.MatcherType;
+import io.split.client.dtos.Partition;
+import io.split.client.dtos.SegmentChange;
+import io.split.client.dtos.Split;
+import io.split.client.dtos.SplitChange;
+import io.split.client.dtos.Status;
import io.split.storages.SegmentCache;
import io.split.storages.memory.SegmentCacheInMemoryImpl;
-import io.split.client.dtos.*;
+import io.split.client.utils.Json;
+import io.split.engine.evaluator.Labels;
import io.split.engine.ConditionsTestUtil;
-import io.split.engine.SDKReadinessGates;
import io.split.engine.matchers.AttributeMatcher;
import io.split.engine.matchers.BetweenMatcher;
import io.split.engine.matchers.CombiningMatcher;
@@ -22,12 +32,14 @@
import io.split.engine.matchers.strings.StartsWithAnyOfMatcher;
import io.split.engine.segments.SegmentChangeFetcher;
import io.split.grammar.Treatments;
-import io.split.telemetry.storage.InMemoryTelemetryStorage;
-import io.split.telemetry.storage.TelemetryStorage;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -36,9 +48,7 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
/**
* Tests for ExperimentParser
@@ -50,11 +60,9 @@ public class SplitParserTest {
public static final String EMPLOYEES = "employees";
public static final String SALES_PEOPLE = "salespeople";
public static final int CONDITIONS_UPPER_LIMIT = 50;
- private static final TelemetryStorage TELEMETRY_STORAGE = Mockito.mock(InMemoryTelemetryStorage.class);
@Test
public void works() {
- SDKReadinessGates gates = new SDKReadinessGates();
SegmentCache segmentCache = new SegmentCacheInMemoryImpl();
segmentCache.updateSegment(EMPLOYEES, Stream.of("adil", "pato", "trevor").collect(Collectors.toList()), new ArrayList<>(), 1L);
segmentCache.updateSegment(SALES_PEOPLE, Stream.of("kunal").collect(Collectors.toList()), new ArrayList<>(), 1L);
@@ -87,12 +95,11 @@ public void works() {
ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>());
- assertThat(actual, is(equalTo(expected)));
+ Assert.assertEquals(actual, expected);
}
@Test
public void worksWithConfig() {
- SDKReadinessGates gates = new SDKReadinessGates();
SegmentCache segmentCache = new SegmentCacheInMemoryImpl();
segmentCache.updateSegment(EMPLOYEES, Stream.of("adil", "pato", "trevor").collect(Collectors.toList()), new ArrayList<>(), 1L);
segmentCache.updateSegment(SALES_PEOPLE, Stream.of("kunal").collect(Collectors.toList()), new ArrayList<>(), 1L);
@@ -129,12 +136,12 @@ public void worksWithConfig() {
ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF,
listOfMatcherAndSplits, "user", 1, 1, configurations, new HashSet<>());
- assertThat(actual, is(equalTo(expected)));
- assertThat(actual.configurations().get("on"), is(equalTo(configurations.get("on"))));
+ Assert.assertEquals(actual, expected);
+ Assert.assertEquals(actual.configurations().get("on"), configurations.get("on"));
}
@Test
- public void works_for_two_conditions() {
+ public void worksForTwoConditions() {
SegmentCache segmentCache = new SegmentCacheInMemoryImpl();
segmentCache.updateSegment(EMPLOYEES, Stream.of("adil", "pato", "trevor").collect(Collectors.toList()), new ArrayList<>(), 1L);
segmentCache.updateSegment(SALES_PEOPLE, Stream.of("kunal").collect(Collectors.toList()), new ArrayList<>(), 1L);
@@ -168,12 +175,11 @@ public void works_for_two_conditions() {
ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfParsedConditions, "user", 1, 1, new HashSet<>());
- assertThat(actual, is(equalTo(expected)));
+ Assert.assertEquals(actual, expected);
}
@Test
- public void success_for_long_conditions() {
- SDKReadinessGates gates = new SDKReadinessGates();
+ public void successForLongConditions() {
SegmentCache segmentCache = new SegmentCacheInMemoryImpl();
segmentCache.updateSegment(EMPLOYEES, Stream.of("adil", "pato", "trevor").collect(Collectors.toList()), new ArrayList<>(), 1L);
segmentCache.updateSegment(SALES_PEOPLE, Stream.of("kunal").collect(Collectors.toList()), new ArrayList<>(), 1L);
@@ -200,8 +206,7 @@ public void success_for_long_conditions() {
@Test
- public void works_with_attributes() {
- SDKReadinessGates gates = new SDKReadinessGates();
+ public void worksWithAttributes() {
SegmentCache segmentCache = new SegmentCacheInMemoryImpl();
segmentCache.updateSegment(EMPLOYEES, Stream.of("adil", "pato", "trevor").collect(Collectors.toList()), new ArrayList<>(), 1L);
segmentCache.updateSegment(SALES_PEOPLE, Stream.of("kunal").collect(Collectors.toList()), new ArrayList<>(), 1L);
@@ -239,17 +244,11 @@ public void works_with_attributes() {
ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>());
- assertThat(actual, is(equalTo(expected)));
+ Assert.assertEquals(actual, expected);
}
@Test
- public void less_than_or_equal_to() {
-
-
-// SegmentSynchronizationTask segmentFetcher = new SegmentSynchronizationTaskImp(fetcherMap);
-// SegmentCache segmentCache = new SegmentCacheInMemoryImpl();
- SDKReadinessGates gates = new SDKReadinessGates();
- SegmentCache segmentCache = new SegmentCacheInMemoryImpl();
+ public void lessThanOrEqualTo() {
SegmentChangeFetcher segmentChangeFetcher = Mockito.mock(SegmentChangeFetcher.class);
SegmentChange segmentChangeEmployee = getSegmentChange(-1L, -1L, EMPLOYEES);
SegmentChange segmentChangeSalesPeople = getSegmentChange(-1L, -1L, SALES_PEOPLE);
@@ -278,16 +277,11 @@ public void less_than_or_equal_to() {
ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>());
- assertThat(actual, is(equalTo(expected)));
+ Assert.assertEquals(actual, expected);
}
@Test
- public void equal_to() {
-
-// SegmentSynchronizationTask segmentFetcher = new SegmentSynchronizationTaskImp(fetcherMap);
-// SegmentCache segmentCache = new SegmentCacheInMemoryImpl();
- SDKReadinessGates gates = new SDKReadinessGates();
- SegmentCache segmentCache = new SegmentCacheInMemoryImpl();
+ public void equalTo() {
SegmentChangeFetcher segmentChangeFetcher = Mockito.mock(SegmentChangeFetcher.class);
SegmentChange segmentChangeEmployee = getSegmentChange(-1L, -1L, EMPLOYEES);
SegmentChange segmentChangeSalesPeople = getSegmentChange(-1L, -1L, SALES_PEOPLE);
@@ -315,16 +309,11 @@ public void equal_to() {
ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>());
- assertThat(actual, is(equalTo(expected)));
+ Assert.assertEquals(actual, expected);
}
@Test
- public void equal_to_negative_number() {
-
-// SegmentSynchronizationTask segmentFetcher = new SegmentSynchronizationTaskImp(fetcherMap);
-// SegmentCache segmentCache = new SegmentCacheInMemoryImpl();
- SDKReadinessGates gates = new SDKReadinessGates();
- SegmentCache segmentCache = new SegmentCacheInMemoryImpl();
+ public void equalToNegativeNumber() {
SegmentChangeFetcher segmentChangeFetcher = Mockito.mock(SegmentChangeFetcher.class);
SegmentChange segmentChangeEmployee = getSegmentChange(-1L, -1L, EMPLOYEES);
SegmentChange segmentChangeSalesPeople = getSegmentChange(-1L, -1L, SALES_PEOPLE);
@@ -351,16 +340,11 @@ public void equal_to_negative_number() {
ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>());
- assertThat(actual, is(equalTo(expected)));
+ Assert.assertEquals(actual, expected);
}
@Test
public void between() {
-
-// SegmentSynchronizationTask segmentFetcher = new SegmentSynchronizationTaskImp(fetcherMap);
-// SegmentCache segmentCache = new SegmentCacheInMemoryImpl();
- SDKReadinessGates gates = new SDKReadinessGates();
- SegmentCache segmentCache = new SegmentCacheInMemoryImpl();
SegmentChangeFetcher segmentChangeFetcher = Mockito.mock(SegmentChangeFetcher.class);
SegmentChange segmentChangeEmployee = getSegmentChange(-1L, -1L, EMPLOYEES);
SegmentChange segmentChangeSalesPeople = getSegmentChange(-1L, -1L, SALES_PEOPLE);
@@ -392,12 +376,11 @@ public void between() {
ParsedSplit expected = ParsedSplit.createParsedSplitForTests("first.name", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>());
- assertThat(actual, is(equalTo(expected)));
+ Assert.assertEquals(actual, expected);
}
@Test
- public void contains_any_of_set() {
-
+ public void containsAnyOfSet() {
ArrayList set = Lists.newArrayList("sms", "voice");
List partitions = Lists.newArrayList(ConditionsTestUtil.partition("on", 100));
@@ -411,12 +394,11 @@ public void contains_any_of_set() {
ContainsAnyOfSetMatcher m = new ContainsAnyOfSetMatcher(set);
- set_matcher_test(c, m);
+ setMatcherTest(c, m);
}
@Test
- public void contains_all_of_set() {
-
+ public void containsAllOfSet() {
ArrayList set = Lists.newArrayList("sms", "voice");
List partitions = Lists.newArrayList(ConditionsTestUtil.partition("on", 100));
@@ -430,12 +412,11 @@ public void contains_all_of_set() {
ContainsAllOfSetMatcher m = new ContainsAllOfSetMatcher(set);
- set_matcher_test(c, m);
+ setMatcherTest(c, m);
}
@Test
- public void equal_to_set() {
-
+ public void equalToSet() {
ArrayList set = Lists.newArrayList("sms", "voice");
List partitions = Lists.newArrayList(ConditionsTestUtil.partition("on", 100));
@@ -449,12 +430,11 @@ public void equal_to_set() {
EqualToSetMatcher m = new EqualToSetMatcher(set);
- set_matcher_test(c, m);
+ setMatcherTest(c, m);
}
@Test
- public void is_part_of_set() {
-
+ public void isPartOfSet() {
ArrayList set = Lists.newArrayList("sms", "voice");
List partitions = Lists.newArrayList(ConditionsTestUtil.partition("on", 100));
@@ -468,12 +448,11 @@ public void is_part_of_set() {
PartOfSetMatcher m = new PartOfSetMatcher(set);
- set_matcher_test(c, m);
+ setMatcherTest(c, m);
}
@Test
- public void starts_with_string() {
-
+ public void startsWithString() {
ArrayList set = Lists.newArrayList("sms", "voice");
List partitions = Lists.newArrayList(ConditionsTestUtil.partition("on", 100));
@@ -487,12 +466,11 @@ public void starts_with_string() {
StartsWithAnyOfMatcher m = new StartsWithAnyOfMatcher(set);
- set_matcher_test(c, m);
+ setMatcherTest(c, m);
}
@Test
- public void ends_with_string() {
-
+ public void endsWithString() {
ArrayList set = Lists.newArrayList("sms", "voice");
List partitions = Lists.newArrayList(ConditionsTestUtil.partition("on", 100));
@@ -506,13 +484,12 @@ public void ends_with_string() {
EndsWithAnyOfMatcher m = new EndsWithAnyOfMatcher(set);
- set_matcher_test(c, m);
+ setMatcherTest(c, m);
}
@Test
- public void contains_string() {
-
+ public void containsString() {
ArrayList set = Lists.newArrayList("sms", "voice");
List partitions = Lists.newArrayList(ConditionsTestUtil.partition("on", 100));
@@ -526,15 +503,144 @@ public void contains_string() {
ContainsAnyOfMatcher m = new ContainsAnyOfMatcher(set);
- set_matcher_test(c, m);
+ setMatcherTest(c, m);
}
- public void set_matcher_test(Condition c, io.split.engine.matchers.Matcher m) {
+ @Test
+ public void UnsupportedMatcher() {
+ SplitParser parser = new SplitParser();
+ String splitWithUndefinedMatcher = "{\"since\":-1,\"till\": 1457726098069,\"splits\": [{ \"changeNumber\": 123, \"trafficTypeName\": \"user\", \"name\": \"some_name\","
+ + "\"trafficAllocation\": 100, \"trafficAllocationSeed\": 123456, \"seed\": 321654, \"status\": \"ACTIVE\","
+ + "\"killed\": false, \"defaultTreatment\": \"off\", \"algo\": 2,\"conditions\": [{ \"partitions\": ["
+ + "{\"treatment\": \"on\", \"size\": 50}, {\"treatment\": \"off\", \"size\": 50}], \"contitionType\": \"ROLLOUT\","
+ + "\"label\": \"some_label\", \"matcherGroup\": { \"matchers\": [{ \"matcherType\": \"UNKNOWN\", \"negate\": false}],"
+ + "\"combiner\": \"AND\"}}], \"sets\": [\"set1\"]}]}";
+ SplitChange change = Json.fromJson(splitWithUndefinedMatcher, SplitChange.class);
+ for (Split split : change.splits) {
+ // should not cause exception
+ ParsedSplit parsedSplit = parser.parse(split);
+ for (ParsedCondition parsedCondition : parsedSplit.parsedConditions()) {
+ assertTrue(parsedCondition.label() == Labels.UNSUPPORTED_MATCHER);
+ for (AttributeMatcher matcher : parsedCondition.matcher().attributeMatchers()) {
+ // Check the matcher is ALL_KEYS
+ assertTrue(matcher.matcher().toString().equals(" in segment all"));
+ }
+ }
+ }
+ }
+
+ @Test
+ public void EqualToSemverMatcher() throws IOException {
+ SplitParser parser = new SplitParser();
+ String splits = new String(Files.readAllBytes(Paths.get("src/test/resources/semver/semver-splits.json")), StandardCharsets.UTF_8);
+ SplitChange change = Json.fromJson(splits, SplitChange.class);
+ for (Split split : change.splits) {
+ // should not cause exception
+ ParsedSplit parsedSplit = parser.parse(split);
+ if (split.name.equals("semver_equalto")) {
+ for (ParsedCondition parsedCondition : parsedSplit.parsedConditions()) {
+ assertTrue(parsedCondition.label().equals("equal to semver"));
+ for (AttributeMatcher matcher : parsedCondition.matcher().attributeMatchers()) {
+ // Check the matcher is ALL_KEYS
+ assertTrue(matcher.matcher().toString().equals(" == semver 1\\.22\\.9"));
+ return;
+ }
+ }
+ }
+ }
+ assertTrue(false);
+ }
+
+ @Test
+ public void GreaterThanOrEqualSemverMatcher() throws IOException {
+ SplitParser parser = new SplitParser();
+ String splits = new String(Files.readAllBytes(Paths.get("src/test/resources/semver/semver-splits.json")), StandardCharsets.UTF_8);
+ SplitChange change = Json.fromJson(splits, SplitChange.class);
+ for (Split split : change.splits) {
+ // should not cause exception
+ ParsedSplit parsedSplit = parser.parse(split);
+ if (split.name.equals("semver_greater_or_equalto")) {
+ for (ParsedCondition parsedCondition : parsedSplit.parsedConditions()) {
+ assertTrue(parsedCondition.label().equals("greater than or equal to semver"));
+ for (AttributeMatcher matcher : parsedCondition.matcher().attributeMatchers()) {
+ // Check the matcher is ALL_KEYS
+ assertTrue(matcher.matcher().toString().equals(" >= semver 1\\.22\\.9"));
+ return;
+ }
+ }
+ }
+ }
+ assertTrue(false);
+ }
+
+ @Test
+ public void LessThanOrEqualSemverMatcher() throws IOException {
+ SplitParser parser = new SplitParser();
+ String splits = new String(Files.readAllBytes(Paths.get("src/test/resources/semver/semver-splits.json")), StandardCharsets.UTF_8);
+ SplitChange change = Json.fromJson(splits, SplitChange.class);
+ for (Split split : change.splits) {
+ // should not cause exception
+ ParsedSplit parsedSplit = parser.parse(split);
+ if (split.name.equals("semver_less_or_equalto")) {
+ for (ParsedCondition parsedCondition : parsedSplit.parsedConditions()) {
+ assertTrue(parsedCondition.label().equals("less than or equal to semver"));
+ for (AttributeMatcher matcher : parsedCondition.matcher().attributeMatchers()) {
+ // Check the matcher is ALL_KEYS
+ assertTrue(matcher.matcher().toString().equals(" <= semver 1\\.22\\.9"));
+ return;
+ }
+ }
+ }
+ }
+ assertTrue(false);
+ }
+
+ @Test
+ public void BetweenSemverMatcher() throws IOException {
+ SplitParser parser = new SplitParser();
+ String splits = new String(Files.readAllBytes(Paths.get("src/test/resources/semver/semver-splits.json")), StandardCharsets.UTF_8);
+ SplitChange change = Json.fromJson(splits, SplitChange.class);
+ for (Split split : change.splits) {
+ // should not cause exception
+ ParsedSplit parsedSplit = parser.parse(split);
+ if (split.name.equals("semver_between")) {
+ for (ParsedCondition parsedCondition : parsedSplit.parsedConditions()) {
+ assertTrue(parsedCondition.label().equals("between semver"));
+ for (AttributeMatcher matcher : parsedCondition.matcher().attributeMatchers()) {
+ // Check the matcher is ALL_KEYS
+ assertTrue(matcher.matcher().toString().equals(" between semver 1\\.22\\.9 and 2\\.1\\.0"));
+ return;
+ }
+ }
+ }
+ }
+ assertTrue(false);
+ }
+
+ @Test
+ public void InListSemverMatcher() throws IOException {
+ SplitParser parser = new SplitParser();
+ String splits = new String(Files.readAllBytes(Paths.get("src/test/resources/semver/semver-splits.json")), StandardCharsets.UTF_8);
+ SplitChange change = Json.fromJson(splits, SplitChange.class);
+ for (Split split : change.splits) {
+ // should not cause exception
+ ParsedSplit parsedSplit = parser.parse(split);
+ if (split.name.equals("semver_inlist")) {
+ for (ParsedCondition parsedCondition : parsedSplit.parsedConditions()) {
+ assertTrue(parsedCondition.label().equals("in list semver"));
+ for (AttributeMatcher matcher : parsedCondition.matcher().attributeMatchers()) {
+ // Check the matcher is ALL_KEYS
+ assertTrue(matcher.matcher().toString().startsWith(" in semver list"));
+ return;
+ }
+ }
+ }
+ }
+ assertTrue(false);
+ }
+
+ public void setMatcherTest(Condition c, io.split.engine.matchers.Matcher m) {
-// SegmentSynchronizationTask segmentFetcher = new SegmentSynchronizationTaskImp(fetcherMap);
-// SegmentCache segmentCache = new SegmentCacheInMemoryImpl();
- SDKReadinessGates gates = new SDKReadinessGates();
- SegmentCache segmentCache = new SegmentCacheInMemoryImpl();
SegmentChangeFetcher segmentChangeFetcher = Mockito.mock(SegmentChangeFetcher.class);
SegmentChange segmentChangeEmployee = getSegmentChange(-1L, -1L, EMPLOYEES);
SegmentChange segmentChangeSalesPeople = getSegmentChange(-1L, -1L, SALES_PEOPLE);
@@ -560,7 +666,7 @@ public void set_matcher_test(Condition c, io.split.engine.matchers.Matcher m) {
ParsedSplit expected = ParsedSplit.createParsedSplitForTests("splitName", 123, false, Treatments.OFF, listOfMatcherAndSplits, "user", 1, 1, new HashSet<>());
- assertThat(actual, is(equalTo(expected)));
+ Assert.assertEquals(actual, expected);
}
private Split makeSplit(String name, int seed, List conditions, long changeNumber) {
@@ -592,5 +698,4 @@ private SegmentChange getSegmentChange(long since, long till, String segmentName
segmentChange.removed = new ArrayList<>();
return segmentChange;
}
-
-}
+}
\ No newline at end of file
diff --git a/client/src/test/java/io/split/engine/matchers/AttributeMatcherTest.java b/client/src/test/java/io/split/engine/matchers/AttributeMatcherTest.java
index 7c723adb6..9f535790d 100644
--- a/client/src/test/java/io/split/engine/matchers/AttributeMatcherTest.java
+++ b/client/src/test/java/io/split/engine/matchers/AttributeMatcherTest.java
@@ -5,6 +5,7 @@
import com.google.common.collect.Maps;
import io.split.client.dtos.DataType;
import io.split.engine.matchers.strings.WhitelistMatcher;
+import org.junit.Assert;
import org.junit.Test;
import java.util.Calendar;
@@ -12,9 +13,6 @@
import java.util.Date;
import java.util.Map;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
/**
* Tests for AllKeysMatcher
*/
@@ -23,60 +21,62 @@ public class AttributeMatcherTest {
@Test
public void works() {
AttributeMatcher matcher = new AttributeMatcher("creation_date", new GreaterThanOrEqualToMatcher(100L, DataType.NUMBER), false);
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", 99L), null), is(false));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", 100L), null), is(true));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", 101), null), is(true));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", 101.3), null), is(false));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", Calendar.getInstance()), null), is(false));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", new Date()), null), is(false));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("creation_date", 99L), null));
+ Assert.assertTrue(matcher.match("ignore", null, ImmutableMap.of("creation_date", 100L), null));
+ Assert.assertTrue(matcher.match("ignore", null, ImmutableMap.of("creation_date", 101), null));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("creation_date", 101.3), null));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("creation_date", Calendar.getInstance()), null));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("creation_date", new Date()), null));
}
@Test
- public void works_negation() {
+ public void worksNegation() {
AttributeMatcher matcher = new AttributeMatcher("creation_date", new GreaterThanOrEqualToMatcher(100L, DataType.NUMBER), true);
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", 99L), null), is(true));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", 100L), null), is(false));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", 101), null), is(false));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", 101.3), null), is(true));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", Calendar.getInstance()), null), is(true));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", new Date()), null), is(true));
+ Assert.assertTrue(matcher.match("ignore", null, ImmutableMap.of("creation_date", 99L), null));
+ Assert.assertTrue(matcher.match("ignore", null, ImmutableMap.of("creation_date", 99L), null));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("creation_date", 100L), null));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("creation_date", 101), null));
+ Assert.assertTrue(matcher.match("ignore", null, ImmutableMap.of("creation_date", 101.3), null));
+ Assert.assertTrue(matcher.match("ignore", null, ImmutableMap.of("creation_date", Calendar.getInstance()), null));
+ Assert.assertTrue(matcher.match("ignore", null, ImmutableMap.of("creation_date", new Date()), null));
}
@Test
- public void works_less_than_or_equal_to() {
+ public void worksLessThanOrEqualTo() {
AttributeMatcher matcher = new AttributeMatcher("creation_date", new LessThanOrEqualToMatcher(100L, DataType.NUMBER), false);
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", 99L), null), is(true));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", 100L), null), is(true));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", 101), null), is(false));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", 101.3), null), is(false));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", Calendar.getInstance()), null), is(false));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", new Date()), null), is(false));
+ Assert.assertTrue(matcher.match("ignore", null, ImmutableMap.of("creation_date", 99L), null));
+ Assert.assertTrue(matcher.match("ignore", null, ImmutableMap.of("creation_date", 100L), null));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("creation_date", 101), null));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("creation_date", 101.3), null));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("creation_date", 101.3), null));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("creation_date", Calendar.getInstance()), null));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("creation_date", new Date()), null));
}
@Test
- public void works_boolean() {
+ public void worksBoolean() {
AttributeMatcher matcher = new AttributeMatcher("value", new BooleanMatcher(true), false);
- assertThat(matcher.match("ignore", null, ImmutableMap.of("value", true), null), is(true));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("value", "true"), null), is(true));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("value", "True"), null), is(true));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("value", "TrUe"), null), is(true));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("value", "TRUE"), null), is(true));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("value", false), null), is(false));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("value", "false"), null), is(false));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("value", "False"), null), is(false));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("value", "FALSE"), null), is(false));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("value", "faLSE"), null), is(false));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("value", ""), null), is(false));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("value", 0), null), is(false));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("value", 1), null), is(false));
+ Assert.assertTrue(matcher.match("ignore", null, ImmutableMap.of("value", true), null));
+ Assert.assertTrue(matcher.match("ignore", null, ImmutableMap.of("value", "true"), null));
+ Assert.assertTrue(matcher.match("ignore", null, ImmutableMap.of("value", "True"), null));
+ Assert.assertTrue(matcher.match("ignore", null, ImmutableMap.of("value", "TrUe"), null));
+ Assert.assertTrue(matcher.match("ignore", null, ImmutableMap.of("value", "TRUE"), null));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("value", false), null));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("value", "false"), null));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("value", "False"), null));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("value", "FALSE"), null));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("value", "faLSE"), null));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("value", ""), null));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("value", 0), null));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("value", 1), null));
}
@Test
- public void error_conditions() {
+ public void errorConditions() {
AttributeMatcher matcher = new AttributeMatcher("creation_date", new GreaterThanOrEqualToMatcher(100L, DataType.NUMBER), false);
- assertThat(matcher.match("ignore", null, null, null), is(false));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("foo", 101), null), is(false));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", "101"), null), is(false));
+ Assert.assertFalse(matcher.match("ignore", null, null, null));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("foo", 101), null));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("creation_date", "101"), null));
}
@Test
@@ -85,38 +85,38 @@ public void dates() {
Calendar c = Calendar.getInstance();
c.add(Calendar.YEAR, -1);
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", c.getTimeInMillis()), null), is(false));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("creation_date", c.getTimeInMillis()), null));
c.add(Calendar.YEAR, 2);
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", c.getTimeInMillis()), null), is(true));
+ Assert.assertTrue(matcher.match("ignore", null, ImmutableMap.of("creation_date", c.getTimeInMillis()), null));
}
@Test
public void between() {
AttributeMatcher matcher = new AttributeMatcher("creation_date", new BetweenMatcher(10, 12, DataType.NUMBER), false);
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", 9), null), is(false));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", 10), null), is(true));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", 11), null), is(true));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", 12), null), is(true));
- assertThat(matcher.match("ignore", null, ImmutableMap.of("creation_date", 13), null), is(false));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("creation_date", 9), null));
+ Assert.assertTrue(matcher.match("ignore", null, ImmutableMap.of("creation_date", 10), null));
+ Assert.assertTrue(matcher.match("ignore", null, ImmutableMap.of("creation_date", 11), null));
+ Assert.assertTrue(matcher.match("ignore", null, ImmutableMap.of("creation_date", 12), null));
+ Assert.assertFalse(matcher.match("ignore", null, ImmutableMap.of("creation_date", 13), null));
}
@Test
- public void when_no_attribute_we_use_the_key() {
+ public void whenNoAttributeWeUseTheKey() {
AttributeMatcher matcher = new AttributeMatcher(null, new WhitelistMatcher(Lists.newArrayList("trial")), false);
Map nullMap = Maps.newHashMap();
nullMap.put("planType", null);
- assertThat(matcher.match("trial", null, ImmutableMap.of("planType", "trial"), null), is(true));
- assertThat(matcher.match("trial", null, ImmutableMap.of("planType", "Trial"), null), is(true));
- assertThat(matcher.match("trial", null, nullMap, null), is(true));
- assertThat(matcher.match("trial", null, ImmutableMap.of("planType", "premium"), null), is(true));
- assertThat(matcher.match("trial", null, ImmutableMap.of("planType", 10), null), is(true));
- assertThat(matcher.match("trial", null, Collections.emptyMap(), null), is(true));
- assertThat(matcher.match("trial", null, null, null), is(true));
- assertThat(matcher.match("premium", null, null, null), is(false));
+ Assert.assertTrue(matcher.match("trial", null, ImmutableMap.of("planType", "trial"), null));
+ Assert.assertTrue(matcher.match("trial", null, ImmutableMap.of("planType", "Trial"), null));
+ Assert.assertTrue(matcher.match("trial", null, nullMap, null));
+ Assert.assertTrue(matcher.match("trial", null, ImmutableMap.of("planType", "premium"), null));
+ Assert.assertTrue(matcher.match("trial", null, ImmutableMap.of("planType", 10), null));
+ Assert.assertTrue(matcher.match("trial", null, Collections.emptyMap(), null));
+ Assert.assertTrue(matcher.match("trial", null, null, null));
+ Assert.assertFalse(matcher.match("premium", null, null, null));
}
-}
+}
\ No newline at end of file
diff --git a/client/src/test/java/io/split/engine/matchers/BetweenMatcherTest.java b/client/src/test/java/io/split/engine/matchers/BetweenMatcherTest.java
index 410977699..22bc3b449 100644
--- a/client/src/test/java/io/split/engine/matchers/BetweenMatcherTest.java
+++ b/client/src/test/java/io/split/engine/matchers/BetweenMatcherTest.java
@@ -1,11 +1,9 @@
package io.split.engine.matchers;
import io.split.client.dtos.DataType;
+import org.junit.Assert;
import org.junit.Test;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
/**
* Tests for BetweenMatcherTest
*/
@@ -18,18 +16,18 @@ public void works() {
BetweenMatcher matcher = new BetweenMatcher(start, end, DataType.NUMBER);
- assertThat(matcher.match(null, null, null, null), is(false));
+ Assert.assertFalse(matcher.match(null, null, null, null));
for (int i = start; i <= end; i++) {
- assertThat(matcher.match(i, null, null, null), is(true));
+ Assert.assertTrue(matcher.match(i, null, null, null));
}
- assertThat(matcher.match(new Long(start - 1), null, null, null), is(false));
- assertThat(matcher.match(end + 1, null, null, null), is(false));
+ Assert.assertFalse(matcher.match(new Long(start - 1), null, null, null));
+ Assert.assertFalse(matcher.match(end + 1, null, null, null));
}
@Test
- public void works_dates() {
+ public void worksDates() {
long april11_2016_23_59 = 1460419199000L;
long april12_2016_midnight_19 = 1460420360000L;
long april12_2016_midnight_20 = 1460420421903L;
@@ -39,14 +37,10 @@ public void works_dates() {
BetweenMatcher matcher = new BetweenMatcher(april12_2016_midnight_19, april12_2016_midnight_20_59, DataType.DATETIME);
- assertThat(matcher.match(april11_2016_23_59, null, null, null), is(false));
- assertThat(matcher.match(april12_2016_midnight_19, null, null, null), is(true));
- assertThat(matcher.match(april12_2016_midnight_20, null, null, null), is(true));
- assertThat(matcher.match(april12_2016_midnight_20_59, null, null, null), is(true));
- assertThat(matcher.match(april12_2016_1_20, null, null, null), is(false));
-
-
+ Assert.assertFalse(matcher.match(april11_2016_23_59, null, null, null));
+ Assert.assertTrue(matcher.match(april12_2016_midnight_19, null, null, null));
+ Assert.assertTrue(matcher.match(april12_2016_midnight_20, null, null, null));
+ Assert.assertTrue(matcher.match(april12_2016_midnight_20_59, null, null, null));
+ Assert.assertFalse(matcher.match(april12_2016_1_20, null, null, null));
}
-
-
-}
+}
\ No newline at end of file
diff --git a/client/src/test/java/io/split/engine/matchers/BetweenSemverMatcherTest.java b/client/src/test/java/io/split/engine/matchers/BetweenSemverMatcherTest.java
new file mode 100644
index 000000000..41a4f76d4
--- /dev/null
+++ b/client/src/test/java/io/split/engine/matchers/BetweenSemverMatcherTest.java
@@ -0,0 +1,36 @@
+package io.split.engine.matchers;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+/**
+ * Tests for EqualToSemverMatcher
+ */
+
+public class BetweenSemverMatcherTest {
+
+ @Test
+ public void works() {
+ BetweenSemverMatcher betweenSemverMatcher = new BetweenSemverMatcher("2.1.8", "3.0.0");
+
+ assertTrue( betweenSemverMatcher.match("2.1.8", null, null, null));
+ assertTrue( betweenSemverMatcher.match("2.1.9", null, null, null));
+ assertFalse( betweenSemverMatcher.match("2.1.8-rc", null, null, null));
+ assertTrue( betweenSemverMatcher.match("3.0.0+build", null, null, null));
+ assertFalse( betweenSemverMatcher.match("4.5.8", null, null, null));
+ assertFalse( betweenSemverMatcher.match("1.0.4", null, null, null));
+ assertTrue(betweenSemverMatcher.equals(betweenSemverMatcher));
+ assertTrue(betweenSemverMatcher.hashCode() != 0);
+ }
+
+ @Test
+ public void testNull() {
+ BetweenSemverMatcher betweenSemverMatcher = new BetweenSemverMatcher("2.1.8", "3.0.0");
+ assertFalse( betweenSemverMatcher.match(null, null, null, null));
+
+ betweenSemverMatcher = new BetweenSemverMatcher("2.www.8", "3.xx.0");
+ assertFalse(betweenSemverMatcher.match("2.www.8", null, null, null));
+ }
+}
diff --git a/client/src/test/java/io/split/engine/matchers/BooleanMatcherTest.java b/client/src/test/java/io/split/engine/matchers/BooleanMatcherTest.java
index cb466bc0b..14889af68 100644
--- a/client/src/test/java/io/split/engine/matchers/BooleanMatcherTest.java
+++ b/client/src/test/java/io/split/engine/matchers/BooleanMatcherTest.java
@@ -8,7 +8,7 @@
public class BooleanMatcherTest {
@Test
- public void works_true() {
+ public void worksTrue() {
BooleanMatcher matcher = new BooleanMatcher(true);
assertThat(matcher.match(null, null, null, null), is(false));
assertThat(matcher.match(true, null, null, null), is(true));
@@ -23,7 +23,7 @@ public void works_true() {
}
@Test
- public void works_false() {
+ public void worksFalse() {
BooleanMatcher matcher = new BooleanMatcher(false);
assertThat(matcher.match(null, null, null, null), is(false));
assertThat(matcher.match(true, null, null, null), is(false));
diff --git a/client/src/test/java/io/split/engine/matchers/CombiningMatcherTest.java b/client/src/test/java/io/split/engine/matchers/CombiningMatcherTest.java
index ee58a18a2..3946aed50 100644
--- a/client/src/test/java/io/split/engine/matchers/CombiningMatcherTest.java
+++ b/client/src/test/java/io/split/engine/matchers/CombiningMatcherTest.java
@@ -3,13 +3,11 @@
import com.google.common.collect.Lists;
import io.split.client.dtos.MatcherCombiner;
import io.split.engine.matchers.strings.WhitelistMatcher;
+import org.junit.Assert;
import org.junit.Test;
import java.util.Collections;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
/**
* Tests CombiningMatcher
*
@@ -18,15 +16,14 @@
public class CombiningMatcherTest {
@Test
- public void works_and() {
+ public void worksAnd() {
AttributeMatcher matcher1 = AttributeMatcher.vanilla(new AllKeysMatcher());
AttributeMatcher matcher2 = AttributeMatcher.vanilla(new WhitelistMatcher(Lists.newArrayList("a", "b")));
CombiningMatcher combiner = new CombiningMatcher(MatcherCombiner.AND, Lists.newArrayList(matcher1, matcher2));
- assertThat(combiner.match("a", null, null, null), is(true));
- assertThat(combiner.match("b", null, Collections.emptyMap(), null), is(true));
- assertThat(combiner.match("c", null, null, null), is(false));
+ Assert.assertTrue(combiner.match("a", null, null, null));
+ Assert.assertTrue(combiner.match("b", null, Collections.emptyMap(), null));
+ Assert.assertFalse(combiner.match("c", null, null, null));
}
-
-}
+}
\ No newline at end of file
diff --git a/client/src/test/java/io/split/engine/matchers/EqualToMatcherTest.java b/client/src/test/java/io/split/engine/matchers/EqualToMatcherTest.java
index aeb887f9c..f8320e511 100644
--- a/client/src/test/java/io/split/engine/matchers/EqualToMatcherTest.java
+++ b/client/src/test/java/io/split/engine/matchers/EqualToMatcherTest.java
@@ -1,11 +1,9 @@
package io.split.engine.matchers;
import io.split.client.dtos.DataType;
+import org.junit.Assert;
import org.junit.Test;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
/**
* Tests for AllKeysMatcher
*/
@@ -14,30 +12,30 @@ public class EqualToMatcherTest {
@Test
public void works() {
EqualToMatcher matcher = new EqualToMatcher(10, DataType.NUMBER);
- assertThat(matcher.match(null, null, null, null), is(false));
- assertThat(matcher.match(1, null, null, null), is(false));
- assertThat(matcher.match(new Long(-1), null, null, null), is(false));
- assertThat(matcher.match(9, null, null, null), is(false));
- assertThat(matcher.match(new Long(10), null, null, null), is(true));
- assertThat(matcher.match(11, null, null, null), is(false));
- assertThat(matcher.match(100, null, null, null), is(false));
+ Assert.assertFalse(matcher.match(null, null, null, null));
+ Assert.assertFalse(matcher.match(1, null, null, null));
+ Assert.assertFalse(matcher.match(new Long(-1), null, null, null));
+ Assert.assertFalse(matcher.match(9, null, null, null));
+ Assert.assertTrue(matcher.match(new Long(10), null, null, null));
+ Assert.assertFalse(matcher.match(11, null, null, null));
+ Assert.assertFalse(matcher.match(100, null, null, null));
}
@Test
- public void works_negative() {
+ public void worksNegative() {
EqualToMatcher matcher = new EqualToMatcher(-10, DataType.NUMBER);
- assertThat(matcher.match(null, null, null, null), is(false));
- assertThat(matcher.match(1, null, null, null), is(false));
- assertThat(matcher.match(new Long(-1), null, null, null), is(false));
- assertThat(matcher.match(9, null, null, null), is(false));
- assertThat(matcher.match(new Long(10), null, null, null), is(false));
- assertThat(matcher.match(11, null, null, null), is(false));
- assertThat(matcher.match(-10, null, null, null), is(true));
- assertThat(matcher.match(-11, null, null, null), is(false));
+ Assert.assertFalse(matcher.match(null, null, null, null));
+ Assert.assertFalse(matcher.match(1, null, null, null));
+ Assert.assertFalse(matcher.match(new Long(-1), null, null, null));
+ Assert.assertFalse(matcher.match(9, null, null, null));
+ Assert.assertFalse(matcher.match(new Long(10), null, null, null));
+ Assert.assertFalse(matcher.match(11, null, null, null));
+ Assert.assertTrue(matcher.match(-10, null, null, null));
+ Assert.assertFalse(matcher.match(-11, null, null, null));
}
@Test
- public void works_dates() {
+ public void worksDates() {
long april11_2016_23_59_59 = 1460419199000L;
long april12_2016_midnight_19 = 1460420360000L;
long april12_2016_midnight_20 = 1460420421903L;
@@ -45,12 +43,10 @@ public void works_dates() {
long april13_2016_00_00_00 = 1460505600000L;
EqualToMatcher matcher = new EqualToMatcher(april12_2016_midnight_20, DataType.DATETIME);
- assertThat(matcher.match(april11_2016_23_59_59, null, null, null), is(false));
- assertThat(matcher.match(april12_2016_midnight_19, null, null, null), is(true));
- assertThat(matcher.match(april12_2016_midnight_20, null, null, null), is(true));
- assertThat(matcher.match(april12_2016_1_20, null, null, null), is(true));
- assertThat(matcher.match(april13_2016_00_00_00, null, null, null), is(false));
-
+ Assert.assertFalse(matcher.match(april11_2016_23_59_59, null, null, null));
+ Assert.assertTrue(matcher.match(april12_2016_midnight_19, null, null, null));
+ Assert.assertTrue(matcher.match(april12_2016_midnight_20, null, null, null));
+ Assert.assertTrue(matcher.match(april12_2016_1_20, null, null, null));
+ Assert.assertFalse(matcher.match(april13_2016_00_00_00, null, null, null));
}
-
-}
+}
\ No newline at end of file
diff --git a/client/src/test/java/io/split/engine/matchers/EqualToSemverMatcherTest.java b/client/src/test/java/io/split/engine/matchers/EqualToSemverMatcherTest.java
new file mode 100644
index 000000000..a5a41e2bb
--- /dev/null
+++ b/client/src/test/java/io/split/engine/matchers/EqualToSemverMatcherTest.java
@@ -0,0 +1,34 @@
+package io.split.engine.matchers;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+/**
+ * Tests for EqualToSemverMatcher
+ */
+
+public class EqualToSemverMatcherTest {
+
+ @Test
+ public void works() {
+ EqualToSemverMatcher equalToSemverMatcher = new EqualToSemverMatcher("2.1.8");
+
+ assertTrue( equalToSemverMatcher.match("2.1.8", null, null, null));
+ assertFalse(equalToSemverMatcher.match("2.1.9", null, null, null));
+ assertFalse(equalToSemverMatcher.match("2.1.8-rc", null, null, null));
+ assertFalse( equalToSemverMatcher.match("2.1.8+build", null, null, null));
+ assertTrue(equalToSemverMatcher.equals(equalToSemverMatcher));
+ assertTrue(equalToSemverMatcher.hashCode() != 0);
+ }
+
+ @Test
+ public void testNull() {
+ EqualToSemverMatcher equalToSemverMatcher = new EqualToSemverMatcher("2.1.8");
+ assertFalse( equalToSemverMatcher.match(null, null, null, null));
+
+ equalToSemverMatcher = new EqualToSemverMatcher("2.ee.8");
+ assertFalse(equalToSemverMatcher.match("2.ee.8", null, null, null));
+ }
+}
diff --git a/client/src/test/java/io/split/engine/matchers/GreaterThanOrEqualToMatcherTest.java b/client/src/test/java/io/split/engine/matchers/GreaterThanOrEqualToMatcherTest.java
index 566f53623..bbe37fdd7 100644
--- a/client/src/test/java/io/split/engine/matchers/GreaterThanOrEqualToMatcherTest.java
+++ b/client/src/test/java/io/split/engine/matchers/GreaterThanOrEqualToMatcherTest.java
@@ -1,11 +1,9 @@
package io.split.engine.matchers;
import io.split.client.dtos.DataType;
+import org.junit.Assert;
import org.junit.Test;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
/**
* Tests for AllKeysMatcher
*/
@@ -14,31 +12,31 @@ public class GreaterThanOrEqualToMatcherTest {
@Test
public void works() {
GreaterThanOrEqualToMatcher matcher = new GreaterThanOrEqualToMatcher(10, DataType.NUMBER);
- assertThat(matcher.match(null, null, null, null), is(false));
- assertThat(matcher.match(1, null, null, null), is(false));
- assertThat(matcher.match(new Long(-1), null, null, null), is(false));
- assertThat(matcher.match(9, null, null, null), is(false));
- assertThat(matcher.match(new Long(10), null, null, null), is(true));
- assertThat(matcher.match(11, null, null, null), is(true));
- assertThat(matcher.match(100, null, null, null), is(true));
+ Assert.assertFalse(matcher.match(null, null, null, null));
+ Assert.assertFalse(matcher.match(1, null, null, null));
+ Assert.assertFalse(matcher.match(new Long(-1), null, null, null));
+ Assert.assertFalse(matcher.match(9, null, null, null));
+ Assert.assertTrue(matcher.match(new Long(10), null, null, null));
+ Assert.assertTrue(matcher.match(11, null, null, null));
+ Assert.assertTrue(matcher.match(100, null, null, null));
}
@Test
- public void works_negative() {
+ public void worksNegative() {
GreaterThanOrEqualToMatcher matcher = new GreaterThanOrEqualToMatcher(-10, DataType.NUMBER);
- assertThat(matcher.match(null, null, null, null), is(false));
- assertThat(matcher.match(1, null, null, null), is(true));
- assertThat(matcher.match(new Long(-1), null, null, null), is(true));
- assertThat(matcher.match(9, null, null, null), is(true));
- assertThat(matcher.match(new Long(10), null, null, null), is(true));
- assertThat(matcher.match(11, null, null, null), is(true));
- assertThat(matcher.match(100, null, null, null), is(true));
- assertThat(matcher.match(-10, null, null, null), is(true));
- assertThat(matcher.match(-11, null, null, null), is(false));
+ Assert.assertFalse(matcher.match(null, null, null, null));
+ Assert.assertTrue(matcher.match(1, null, null, null));
+ Assert.assertTrue(matcher.match(new Long(-1), null, null, null));
+ Assert.assertTrue(matcher.match(9, null, null, null));
+ Assert.assertTrue(matcher.match(new Long(10), null, null, null));
+ Assert.assertTrue(matcher.match(11, null, null, null));
+ Assert.assertTrue(matcher.match(100, null, null, null));
+ Assert.assertTrue(matcher.match(-10, null, null, null));
+ Assert.assertFalse(matcher.match(-11, null, null, null));
}
@Test
- public void works_dates() {
+ public void worksDates() {
long april12_2016_midnight_19 = 1460420360000L;
long april12_2016_midnight_20 = 1460420421903L;
long april12_2016_midnight_20_59 = 1460420459000L;
@@ -46,11 +44,11 @@ public void works_dates() {
long april12_2016_18_20 = 1460485239000L;
GreaterThanOrEqualToMatcher matcher = new GreaterThanOrEqualToMatcher(april12_2016_midnight_20, DataType.DATETIME);
- assertThat(matcher.match(april12_2016_midnight_19, null, null, null), is(false));
- assertThat(matcher.match(april12_2016_midnight_20_59, null, null, null), is(true));
- assertThat(matcher.match(april12_2016_midnight_20, null, null, null), is(true));
- assertThat(matcher.match(april12_2016_1_20, null, null, null), is(true));
- assertThat(matcher.match(april12_2016_18_20, null, null, null), is(true));
+ Assert.assertFalse(matcher.match(april12_2016_midnight_19, null, null, null));
+ Assert.assertTrue(matcher.match(april12_2016_midnight_20_59, null, null, null));
+ Assert.assertTrue(matcher.match(april12_2016_midnight_20_59, null, null, null));
+ Assert.assertTrue(matcher.match(april12_2016_midnight_20, null, null, null));
+ Assert.assertTrue(matcher.match(april12_2016_1_20, null, null, null));
+ Assert.assertTrue(matcher.match(april12_2016_18_20, null, null, null));
}
-
-}
+}
\ No newline at end of file
diff --git a/client/src/test/java/io/split/engine/matchers/GreaterThanOrEqualToSemverMatcherTest.java b/client/src/test/java/io/split/engine/matchers/GreaterThanOrEqualToSemverMatcherTest.java
new file mode 100644
index 000000000..753034c70
--- /dev/null
+++ b/client/src/test/java/io/split/engine/matchers/GreaterThanOrEqualToSemverMatcherTest.java
@@ -0,0 +1,34 @@
+package io.split.engine.matchers;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+/**
+ * Tests for EqualToSemverMatcher
+ */
+
+public class GreaterThanOrEqualToSemverMatcherTest {
+
+ @Test
+ public void works() {
+ GreaterThanOrEqualToSemverMatcher greaterThanOrEqualToSemverMatcher = new GreaterThanOrEqualToSemverMatcher("2.1.8");
+ assertTrue( greaterThanOrEqualToSemverMatcher.match("2.1.8", null, null, null));
+ assertTrue( greaterThanOrEqualToSemverMatcher.match("2.1.9", null, null, null));
+ assertFalse( greaterThanOrEqualToSemverMatcher.match("2.1.8-rc", null, null, null));
+ assertFalse( greaterThanOrEqualToSemverMatcher.match("2.0.10", null, null, null));
+ assertTrue( greaterThanOrEqualToSemverMatcher.match("2.1.8+build", null, null, null));
+ assertTrue(greaterThanOrEqualToSemverMatcher.equals(greaterThanOrEqualToSemverMatcher));
+ assertTrue(greaterThanOrEqualToSemverMatcher.hashCode() != 0);
+ }
+
+ @Test
+ public void testNull() {
+ GreaterThanOrEqualToSemverMatcher greaterThanOrEqualToSemverMatcher = new GreaterThanOrEqualToSemverMatcher("2.1.8");
+ assertFalse( greaterThanOrEqualToSemverMatcher.match(null, null, null, null));
+
+ greaterThanOrEqualToSemverMatcher = new GreaterThanOrEqualToSemverMatcher("2.ee.8");
+ assertFalse(greaterThanOrEqualToSemverMatcher.match("2.ee.8", null, null, null));
+ }
+}
diff --git a/client/src/test/java/io/split/engine/matchers/InListSemverMatcherTest.java b/client/src/test/java/io/split/engine/matchers/InListSemverMatcherTest.java
new file mode 100644
index 000000000..c01371251
--- /dev/null
+++ b/client/src/test/java/io/split/engine/matchers/InListSemverMatcherTest.java
@@ -0,0 +1,41 @@
+package io.split.engine.matchers;
+
+import com.google.common.collect.Lists;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for EqualToSemverMatcher
+ */
+
+public class InListSemverMatcherTest {
+
+ @Test
+ public void works() {
+ List whitelist = Lists.newArrayList("2.1.8", "3.4.0");
+ InListSemverMatcher inListSemverMatcher = new InListSemverMatcher(whitelist);
+
+ assertTrue( inListSemverMatcher.match("2.1.8", null, null, null) == true);
+ assertTrue( inListSemverMatcher.match("2.1.9", null, null, null) == false);
+ assertTrue( inListSemverMatcher.match("2.1.8-rc", null, null, null) == false);
+ assertTrue( inListSemverMatcher.match("3.4.0", null, null, null) == true);
+ assertTrue( inListSemverMatcher.match("3.4.0+build", null, null, null) == false);
+ assertTrue(inListSemverMatcher.equals(inListSemverMatcher));
+ assertTrue(inListSemverMatcher.hashCode() != 0);
+ }
+
+ @Test
+ public void testNull() {
+ List whitelist = Lists.newArrayList("2.1.8", "3.4.0");
+ InListSemverMatcher inListSemverMatcher = new InListSemverMatcher(whitelist);
+ assertFalse( inListSemverMatcher.match(null, null, null, null));
+
+ whitelist = Lists.newArrayList("2.1.eee", "3.xxx.0");
+ inListSemverMatcher = new InListSemverMatcher(whitelist);
+ assertFalse(inListSemverMatcher.match("2.1.eee", null, null, null));
+ }
+}
diff --git a/client/src/test/java/io/split/engine/matchers/LessThanOrEqualToMatcherTest.java b/client/src/test/java/io/split/engine/matchers/LessThanOrEqualToMatcherTest.java
index 1663a6085..47853ed4c 100644
--- a/client/src/test/java/io/split/engine/matchers/LessThanOrEqualToMatcherTest.java
+++ b/client/src/test/java/io/split/engine/matchers/LessThanOrEqualToMatcherTest.java
@@ -1,11 +1,9 @@
package io.split.engine.matchers;
import io.split.client.dtos.DataType;
+import org.junit.Assert;
import org.junit.Test;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
/**
* Tests for AllKeysMatcher
*/
@@ -14,31 +12,31 @@ public class LessThanOrEqualToMatcherTest {
@Test
public void works() {
LessThanOrEqualToMatcher matcher = new LessThanOrEqualToMatcher(10, DataType.NUMBER);
- assertThat(matcher.match(null, null, null, null), is(false));
- assertThat(matcher.match(1, null, null, null), is(true));
- assertThat(matcher.match(new Long(-1), null, null, null), is(true));
- assertThat(matcher.match(9, null, null, null), is(true));
- assertThat(matcher.match(new Long(10), null, null, null), is(true));
- assertThat(matcher.match(11, null, null, null), is(false));
- assertThat(matcher.match(100, null, null, null), is(false));
+ Assert.assertFalse(matcher.match(null, null, null, null));
+ Assert.assertTrue(matcher.match(1, null, null, null));
+ Assert.assertTrue(matcher.match(new Long(-1), null, null, null));
+ Assert.assertTrue(matcher.match(9, null, null, null));
+ Assert.assertTrue(matcher.match(new Long(10), null, null, null));
+ Assert.assertFalse(matcher.match(11, null, null, null));
+ Assert.assertFalse(matcher.match(100, null, null, null));
}
@Test
- public void works_negative() {
+ public void worksNegative() {
LessThanOrEqualToMatcher matcher = new LessThanOrEqualToMatcher(-10, DataType.NUMBER);
- assertThat(matcher.match(null, null, null, null), is(false));
- assertThat(matcher.match(1, null, null, null), is(false));
- assertThat(matcher.match(new Long(-1), null, null, null), is(false));
- assertThat(matcher.match(9, null, null, null), is(false));
- assertThat(matcher.match(new Long(10), null, null, null), is(false));
- assertThat(matcher.match(11, null, null, null), is(false));
- assertThat(matcher.match(-9, null, null, null), is(false));
- assertThat(matcher.match(-10, null, null, null), is(true));
- assertThat(matcher.match(-11, null, null, null), is(true));
+ Assert.assertFalse(matcher.match(null, null, null, null));
+ Assert.assertFalse(matcher.match(1, null, null, null));
+ Assert.assertFalse(matcher.match(new Long(-1), null, null, null));
+ Assert.assertFalse(matcher.match(9, null, null, null));
+ Assert.assertFalse(matcher.match(new Long(10), null, null, null));
+ Assert.assertFalse(matcher.match(11, null, null, null));
+ Assert.assertFalse(matcher.match(-9, null, null, null));
+ Assert.assertTrue(matcher.match(-10, null, null, null));
+ Assert.assertTrue(matcher.match(-11, null, null, null));
}
@Test
- public void works_dates() {
+ public void worksDates() {
long april11_2016_23_59 = 1460419199000L;
long april12_2016_midnight_19 = 1460420360000L;
long april12_2016_midnight_20 = 1460420421903L;
@@ -46,12 +44,10 @@ public void works_dates() {
long april12_2016_1_20 = 1460424039000L;
LessThanOrEqualToMatcher matcher = new LessThanOrEqualToMatcher(april12_2016_midnight_20, DataType.DATETIME);
- assertThat(matcher.match(april11_2016_23_59, null, null, null), is(true));
- assertThat(matcher.match(april12_2016_midnight_19, null, null, null), is(true));
- assertThat(matcher.match(april12_2016_midnight_20, null, null, null), is(true));
- assertThat(matcher.match(april12_2016_midnight_20_59, null, null, null), is(true));
- assertThat(matcher.match(april12_2016_1_20, null, null, null), is(false));
+ Assert.assertTrue(matcher.match(april11_2016_23_59, null, null, null));
+ Assert.assertTrue(matcher.match(april12_2016_midnight_19, null, null, null));
+ Assert.assertTrue(matcher.match(april12_2016_midnight_20, null, null, null));
+ Assert.assertTrue(matcher.match(april12_2016_midnight_20_59, null, null, null));
+ Assert.assertFalse(matcher.match(april12_2016_1_20, null, null, null));
}
-
-
-}
+}
\ No newline at end of file
diff --git a/client/src/test/java/io/split/engine/matchers/LessThanOrEqualToSemverMatcherTest.java b/client/src/test/java/io/split/engine/matchers/LessThanOrEqualToSemverMatcherTest.java
new file mode 100644
index 000000000..349a608ae
--- /dev/null
+++ b/client/src/test/java/io/split/engine/matchers/LessThanOrEqualToSemverMatcherTest.java
@@ -0,0 +1,35 @@
+package io.split.engine.matchers;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+/**
+ * Tests for EqualToSemverMatcher
+ */
+
+public class LessThanOrEqualToSemverMatcherTest {
+
+ @Test
+ public void works() {
+ LessThanOrEqualToSemverMatcher lessThanOrEqualToSemverMatcher = new LessThanOrEqualToSemverMatcher("2.1.8");
+
+ assertTrue( lessThanOrEqualToSemverMatcher.match("2.1.8", null, null, null));
+ assertFalse( lessThanOrEqualToSemverMatcher.match("2.1.9", null, null, null));
+ assertTrue( lessThanOrEqualToSemverMatcher.match("2.1.8-rc", null, null, null));
+ assertTrue( lessThanOrEqualToSemverMatcher.match("2.0.10", null, null, null));
+ assertTrue( lessThanOrEqualToSemverMatcher.match("2.1.8+build", null, null, null));
+ assertTrue(lessThanOrEqualToSemverMatcher.equals(lessThanOrEqualToSemverMatcher));
+ assertTrue(lessThanOrEqualToSemverMatcher.hashCode() != 0);
+ }
+
+ @Test
+ public void testNull() {
+ LessThanOrEqualToSemverMatcher lessThanOrEqualToSemverMatcher = new LessThanOrEqualToSemverMatcher("2.1.8");
+ assertFalse( lessThanOrEqualToSemverMatcher.match(null, null, null, null));
+
+ lessThanOrEqualToSemverMatcher = new LessThanOrEqualToSemverMatcher("2.ee.8");
+ assertFalse(lessThanOrEqualToSemverMatcher.match("2.ee.8", null, null, null));
+ }
+}
diff --git a/client/src/test/java/io/split/engine/matchers/NegatableMatcherTest.java b/client/src/test/java/io/split/engine/matchers/NegatableMatcherTest.java
index 3aec5ab0d..97e4aebe2 100644
--- a/client/src/test/java/io/split/engine/matchers/NegatableMatcherTest.java
+++ b/client/src/test/java/io/split/engine/matchers/NegatableMatcherTest.java
@@ -6,6 +6,7 @@
import io.split.engine.matchers.strings.WhitelistMatcher;
import io.split.storages.SegmentCache;
import io.split.storages.memory.SegmentCacheInMemoryImpl;
+import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
@@ -13,9 +14,6 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
/**
* Tests for NegatableMatcher.
*
@@ -24,7 +22,7 @@
public class NegatableMatcherTest {
@Test
- public void works_all_keys() {
+ public void worksAllKeys() {
AllKeysMatcher delegate = new AllKeysMatcher();
AttributeMatcher.NegatableMatcher matcher = new AttributeMatcher.NegatableMatcher(delegate, true);
@@ -32,7 +30,7 @@ public void works_all_keys() {
}
@Test
- public void works_segment() {
+ public void worksSegment() {
SegmentCache segmentCache = new SegmentCacheInMemoryImpl();
segmentCache.updateSegment("foo", Stream.of("a","b").collect(Collectors.toList()), new ArrayList<>(), 1L);
UserDefinedSegmentMatcher delegate = new UserDefinedSegmentMatcher("foo");
@@ -44,7 +42,7 @@ public void works_segment() {
}
@Test
- public void works_whitelist() {
+ public void worksWhitelist() {
WhitelistMatcher delegate = new WhitelistMatcher(Lists.newArrayList("a", "b"));
AttributeMatcher.NegatableMatcher matcher = new AttributeMatcher.NegatableMatcher(delegate, true);
@@ -54,9 +52,8 @@ public void works_whitelist() {
}
private void test(AttributeMatcher.NegatableMatcher negationMatcher, String key, boolean expected, SegmentCache segmentCache) {
- assertThat(negationMatcher.match(key, null, null, new EvaluationContext(Mockito.mock(Evaluator.class), segmentCache)), is(expected));
- assertThat(negationMatcher.delegate().match(key, null, null, new EvaluationContext(Mockito.mock(Evaluator.class), segmentCache)), is(!expected));
-
+ Assert.assertEquals(expected, negationMatcher.match(key, null, null, new EvaluationContext(Mockito.mock(Evaluator.class), segmentCache)));
+ Assert.assertNotEquals(expected, negationMatcher.delegate().match(key, null, null, new EvaluationContext(Mockito.mock(Evaluator.class), segmentCache)));
}
diff --git a/client/src/test/java/io/split/engine/matchers/SemverTest.java b/client/src/test/java/io/split/engine/matchers/SemverTest.java
new file mode 100644
index 000000000..40da82643
--- /dev/null
+++ b/client/src/test/java/io/split/engine/matchers/SemverTest.java
@@ -0,0 +1,112 @@
+package io.split.engine.matchers;
+
+import org.junit.Test;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for AllKeysMatcher
+ */
+public class SemverTest {
+
+ @Test
+ public void testValidVersions() throws IOException {
+ List> versions = new ArrayList<>();
+ BufferedReader br = new BufferedReader(new FileReader("src/test/resources/semver/valid-semantic-versions.csv"));
+ String line;
+ boolean firstLine = true;
+ while ((line = br.readLine()) != null) {
+ if (firstLine) {firstLine = false; continue; }
+ String[] values = line.split(",");
+ versions.add(Arrays.asList(values));
+ }
+ for(List version : versions) {
+ assertTrue(Semver.build(version.get(0)) != null);
+ assertTrue(Semver.build(version.get(1)) != null);
+ }
+ }
+
+ @Test
+ public void testInvalidVersions() throws IOException {
+ List> versions = new ArrayList<>();
+ BufferedReader br = new BufferedReader(new FileReader("src/test/resources/semver/invalid-semantic-versions.csv"));
+ String line;
+ boolean firstLine = true;
+ while ((line = br.readLine()) != null) {
+ if (firstLine) {firstLine = false; continue; }
+ String[] values = line.split(",");
+ versions.add(Arrays.asList(values));
+ }
+ for(List version : versions) {
+ assertTrue(Semver.build(version.get(0)) == null);
+ }
+ }
+
+ @Test
+ public void testCompareVersions() throws IOException {
+ List> versions = new ArrayList<>();
+ BufferedReader br = new BufferedReader(new FileReader("src/test/resources/semver/valid-semantic-versions.csv"));
+ String line;
+ boolean firstLine = true;
+ while ((line = br.readLine()) != null) {
+ if (firstLine) {firstLine = false; continue; }
+ String[] values = line.split(",");
+ versions.add(Arrays.asList(values));
+ }
+ for(List version : versions) {
+ assertTrue(Semver.build(version.get(0)).compare(Semver.build(version.get(1))) == 1);
+ assertTrue(Semver.build(version.get(1)).compare(Semver.build(version.get(0))) == -1);
+ }
+
+ versions.clear();
+ br = new BufferedReader(new FileReader("src/test/resources/semver/equal-to-semver.csv"));
+ firstLine = true;
+ while ((line = br.readLine()) != null) {
+ if (firstLine) {firstLine = false; continue; }
+ String[] values = line.split(",");
+ versions.add(Arrays.asList(values));
+ }
+ for(List version : versions) {
+ Semver version1 = Semver.build(version.get(0));
+ Semver version2 = Semver.build(version.get(1));
+
+ if (version.get(2).equals("true")) {
+ assertTrue(version1.version().equals(version2.version()));
+ } else {
+ assertTrue(!version1.version().equals(version2.version()));
+ }
+ }
+
+ versions.clear();
+ br = new BufferedReader(new FileReader("src/test/resources/semver/between-semver.csv"));
+ firstLine = true;
+ while ((line = br.readLine()) != null) {
+ if (firstLine) {firstLine = false; continue; }
+ String[] values = line.split(",");
+ versions.add(Arrays.asList(values));
+ }
+ for(List version : versions) {
+ Semver version1 = Semver.build(version.get(0));
+ Semver version2 = Semver.build(version.get(1));
+ Semver version3 = Semver.build(version.get(2));
+
+ if (version.get(3).equals("true")) {
+ assertTrue(version2.compare(version1) >= 0 && version3.compare(version2) >= 0);
+ } else {
+ assertTrue(version2.compare(version1) < 0 || version3.compare(version2) < 0);
+ }
+ }
+
+ }
+ @Test
+ public void testLeadingZeros() {
+ assertTrue(Semver.build("1.01.2").version().equals("1\\.1\\.2"));
+ assertTrue(Semver.build("1.01.2-rc.01").version().equals("1\\.1\\.2-rc\\.1"));
+ }
+}
diff --git a/client/src/test/java/io/split/engine/matchers/collections/ContainsAllOfSetMatcherTest.java b/client/src/test/java/io/split/engine/matchers/collections/ContainsAllOfSetMatcherTest.java
index caa7b40da..1c84cf2e6 100644
--- a/client/src/test/java/io/split/engine/matchers/collections/ContainsAllOfSetMatcherTest.java
+++ b/client/src/test/java/io/split/engine/matchers/collections/ContainsAllOfSetMatcherTest.java
@@ -1,5 +1,6 @@
package io.split.engine.matchers.collections;
+import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
@@ -7,77 +8,75 @@
import java.util.List;
import java.util.Set;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
/**
* Created by adilaijaz on 4/18/17.
*/
public class ContainsAllOfSetMatcherTest {
@Test
- public void works_for_sets() {
+ public void worksForSets() {
Set set = new HashSet<>();
set.add("first");
set.add("second");
ContainsAllOfSetMatcher matcher = new ContainsAllOfSetMatcher(set);
- assertThat(matcher.match(null, null, null, null), is(false));
+ Assert.assertFalse(matcher.match(null, null, null, null));
Set argument = new HashSet<>();
- assertThat(matcher.match(argument, null, null, null), is(false));
+ Assert.assertFalse(matcher.match(argument, null, null, null));
argument.add("second");
- assertThat(matcher.match(argument, null, null, null), is(false));
+ Assert.assertFalse(matcher.match(argument, null, null, null));
argument.add("first");
- assertThat(matcher.match(argument, null, null, null), is(true));
+ Assert.assertTrue(matcher.match(argument, null, null, null));
argument.add("third");
- assertThat(matcher.match(argument, null, null, null), is(true));
+ Assert.assertTrue(matcher.match(argument, null, null, null));
}
@Test
- public void works_for_lists() {
+ public void worksForLists() {
List