diff --git a/CHANGES.txt b/CHANGES.txt index 44b8d4593..1fcca8ad2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,8 @@ +4.9.0 (Sep 7, 2023) +- Added InputStream config for localhost mode providing a solution when the file is inside a jar. +- Fixed track impressions to send all impressions to the listener. +- Fixed SyncManager shutdown to stop SSE only when is streaming mode on. + 4.8.1 (Aug 1, 2023) - Applied linting rules to the code. - Fixed an issue when the prefix is empty for Redis settings. diff --git a/client/pom.xml b/client/pom.xml index a775f5fb4..5808ac722 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -5,7 +5,7 @@ io.split.client java-client-parent - 4.8.1 + 4.9.0 java-client jar diff --git a/client/src/main/java/io/split/client/JsonLocalhostSplitChangeFetcher.java b/client/src/main/java/io/split/client/JsonLocalhostSplitChangeFetcher.java index 9b42fa4f2..e2cb5d5c9 100644 --- a/client/src/main/java/io/split/client/JsonLocalhostSplitChangeFetcher.java +++ b/client/src/main/java/io/split/client/JsonLocalhostSplitChangeFetcher.java @@ -2,6 +2,7 @@ import com.google.gson.stream.JsonReader; import io.split.client.dtos.SplitChange; +import io.split.client.utils.InputStreamProvider; import io.split.client.utils.Json; import io.split.client.utils.LocalhostSanitizer; import io.split.engine.common.FetchOptions; @@ -9,10 +10,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.UnsupportedEncodingException; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; @@ -20,37 +20,26 @@ public class JsonLocalhostSplitChangeFetcher implements SplitChangeFetcher { private static final Logger _log = LoggerFactory.getLogger(JsonLocalhostSplitChangeFetcher.class); - private final File _file; + private final InputStreamProvider _inputStreamProvider; private byte [] lastHash; - public JsonLocalhostSplitChangeFetcher(String filePath) { - _file = new File(filePath); + public JsonLocalhostSplitChangeFetcher(InputStreamProvider inputStreamProvider) { + _inputStreamProvider = inputStreamProvider; lastHash = new byte[0]; } @Override public SplitChange fetch(long since, FetchOptions options) { - try { - JsonReader jsonReader = new JsonReader(new FileReader(_file)); + JsonReader jsonReader = new JsonReader(new BufferedReader(new InputStreamReader(_inputStreamProvider.get(), StandardCharsets.UTF_8))); SplitChange splitChange = Json.fromJson(jsonReader, SplitChange.class); return processSplitChange(splitChange, since); - } catch (FileNotFoundException f){ - _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", - _file.getPath(), _file.getPath()), f); - throw new IllegalStateException("Problem fetching splitChanges: " + f.getMessage(), f); } catch (Exception e) { - _log.warn(String.format("Problem to fetch split change using the file %s", - _file.getPath()), e); throw new IllegalStateException("Problem fetching splitChanges: " + e.getMessage(), e); } } - private SplitChange processSplitChange(SplitChange splitChange, long changeNumber) throws NoSuchAlgorithmException, UnsupportedEncodingException { + private SplitChange processSplitChange(SplitChange splitChange, long changeNumber) throws NoSuchAlgorithmException { SplitChange splitChangeToProcess = LocalhostSanitizer.sanitization(splitChange); // if the till is less than storage CN and different from the default till ignore the change if (splitChangeToProcess.till < changeNumber && splitChangeToProcess.till != -1) { diff --git a/client/src/main/java/io/split/client/SplitClientConfig.java b/client/src/main/java/io/split/client/SplitClientConfig.java index b362a2de4..4d04b5897 100644 --- a/client/src/main/java/io/split/client/SplitClientConfig.java +++ b/client/src/main/java/io/split/client/SplitClientConfig.java @@ -2,6 +2,7 @@ import io.split.client.impressions.ImpressionListener; import io.split.client.impressions.ImpressionsManager; +import io.split.client.utils.FileTypeEnum; import io.split.integrations.IntegrationsConfig; import io.split.storages.enums.OperationMode; import io.split.storages.enums.StorageMode; @@ -9,6 +10,7 @@ import pluggable.CustomStorageWrapper; import java.io.IOException; +import java.io.InputStream; import java.util.Properties; import java.util.concurrent.ThreadFactory; @@ -49,6 +51,8 @@ public class SplitClientConfig { private final int _maxStringLength; private final boolean _destroyOnShutDown; private final String _splitFile; + private final FileTypeEnum _fileType; + private final InputStream _inputStream; private final String _segmentDirectory; private final IntegrationsConfig _integrationsConfig; private final boolean _streamingEnabled; @@ -81,7 +85,6 @@ public class SplitClientConfig { public static String splitSdkVersion; private final long _lastSeenCacheSize; - public static Builder builder() { return new Builder(); } @@ -111,6 +114,8 @@ private SplitClientConfig(String endpoint, int maxStringLength, boolean destroyOnShutDown, String splitFile, + FileTypeEnum fileType, + InputStream inputStream, String segmentDirectory, IntegrationsConfig integrationsConfig, boolean streamingEnabled, @@ -159,6 +164,8 @@ private SplitClientConfig(String endpoint, _maxStringLength = maxStringLength; _destroyOnShutDown = destroyOnShutDown; _splitFile = splitFile; + _fileType = fileType; + _inputStream = inputStream; _segmentDirectory = segmentDirectory; _integrationsConfig = integrationsConfig; _streamingEnabled = streamingEnabled; @@ -301,6 +308,14 @@ public String splitFile() { return _splitFile; } + public FileTypeEnum fileType() { + return _fileType; + } + + public InputStream inputStream(){ + return _inputStream; + } + public String segmentDirectory() { return _segmentDirectory; } @@ -394,6 +409,8 @@ public static final class Builder { private int _maxStringLength = 250; private boolean _destroyOnShutDown = true; private String _splitFile = null; + private FileTypeEnum _fileType = null; + private InputStream _inputStream = null; private String _segmentDirectory = null; private IntegrationsConfig _integrationsConfig = null; private boolean _streamingEnabled = true; @@ -748,6 +765,12 @@ public Builder splitFile(String splitFile) { return this; } + public Builder splitFile(InputStream inputStream, FileTypeEnum fileType) { + _fileType = fileType; + _inputStream = inputStream; + return this; + } + /** * Set the location of the directory where are the segment json files for localhost mode. * This setting is optional. @@ -1005,6 +1028,8 @@ public SplitClientConfig build() { _maxStringLength, _destroyOnShutDown, _splitFile, + _fileType, + _inputStream, _segmentDirectory, _integrationsConfig, _streamingEnabled, diff --git a/client/src/main/java/io/split/client/SplitFactoryImpl.java b/client/src/main/java/io/split/client/SplitFactoryImpl.java index 3c46ff7fd..88413bdbd 100644 --- a/client/src/main/java/io/split/client/SplitFactoryImpl.java +++ b/client/src/main/java/io/split/client/SplitFactoryImpl.java @@ -1,5 +1,6 @@ package io.split.client; +import com.google.common.io.Files; import io.split.client.dtos.Metadata; import io.split.client.events.EventsSender; import io.split.client.events.EventsStorage; @@ -30,7 +31,11 @@ import io.split.client.interceptors.GzipDecoderResponseInterceptor; import io.split.client.interceptors.GzipEncoderRequestInterceptor; import io.split.client.interceptors.SdkMetadataInterceptorFilter; +import io.split.client.utils.FileInputStreamProvider; +import io.split.client.utils.FileTypeEnum; +import io.split.client.utils.InputStreamProvider; import io.split.client.utils.SDKMetadata; +import io.split.client.utils.StaticContentInputStreamProvider; import io.split.engine.SDKReadinessGates; import io.split.engine.common.ConsumerSyncManager; import io.split.engine.common.ConsumerSynchronizer; @@ -99,6 +104,7 @@ import pluggable.CustomStorageWrapper; import java.io.IOException; +import java.io.InputStream; import java.net.InetAddress; import java.net.URI; import java.net.URISyntaxException; @@ -111,6 +117,8 @@ 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 final static long SSE_CONNECT_TIMEOUT = 30000; private final static long SSE_SOCKET_TIMEOUT = 70000; @@ -366,16 +374,7 @@ protected SplitFactoryImpl(SplitClientConfig config) { config.getThreadFactory()); // SplitFetcher - SplitChangeFetcher splitChangeFetcher; - String splitFile = config.splitFile(); - if (splitFile != null && splitFile.toLowerCase().endsWith(".json")){ - splitChangeFetcher = new JsonLocalhostSplitChangeFetcher(config.splitFile()); - } else if (splitFile != null && !splitFile.isEmpty() && (splitFile.endsWith(".yaml") || splitFile.endsWith(".yml"))) { - splitChangeFetcher = new YamlLocalhostSplitChangeFetcher(splitFile); - } else { - splitChangeFetcher = new LegacyLocalhostSplitChangeFetcher(config.splitFile()); - } - + SplitChangeFetcher splitChangeFetcher = createSplitChangeFetcher(config); SplitParser splitParser = new SplitParser(); _splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCache, _telemetryStorageProducer); @@ -643,4 +642,53 @@ private UniqueKeysTracker createUniqueKeysTracker(SplitClientConfig config){ } return null; } + + private SplitChangeFetcher createSplitChangeFetcher(SplitClientConfig splitClientConfig) { + String splitFile = splitClientConfig.splitFile(); + InputStream inputStream = splitClientConfig.inputStream(); + FileTypeEnum fileType = splitClientConfig.fileType(); + InputStreamProvider inputStreamProvider; + if (splitFile != null || !isInputStreamConfigValid(inputStream, fileType)) { + if (splitFile == null) { + _log.warn("The InputStream config is invalid"); + } + fileType = getFileTypeFromFileName(splitFile); + inputStreamProvider = new FileInputStreamProvider(splitFile); + } else { + inputStreamProvider = new StaticContentInputStreamProvider(inputStream); + } + try { + switch (fileType){ + case JSON: + return new JsonLocalhostSplitChangeFetcher(inputStreamProvider); + case YAML: + return new YamlLocalhostSplitChangeFetcher(inputStreamProvider); + default: + _log.warn(LEGACY_LOG_MESSAGE); + return new LegacyLocalhostSplitChangeFetcher(splitFile); + } + } 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", + splitFile, splitFile), e); + } + _log.warn(LEGACY_LOG_MESSAGE); + return new LegacyLocalhostSplitChangeFetcher(splitFile); + } + + private Boolean isInputStreamConfigValid(InputStream inputStream, FileTypeEnum fileType) { + return inputStream != null && fileType != null; + } + + private FileTypeEnum getFileTypeFromFileName(String fileName) { + try { + return FileTypeEnum.valueOf(Files.getFileExtension(fileName).toUpperCase()); + } catch (Exception e) { + return FileTypeEnum.LEGACY; + } + + } } \ No newline at end of file diff --git a/client/src/main/java/io/split/client/YamlLocalhostSplitChangeFetcher.java b/client/src/main/java/io/split/client/YamlLocalhostSplitChangeFetcher.java index abde47d73..e90ca1389 100644 --- a/client/src/main/java/io/split/client/YamlLocalhostSplitChangeFetcher.java +++ b/client/src/main/java/io/split/client/YamlLocalhostSplitChangeFetcher.java @@ -5,16 +5,15 @@ import io.split.client.dtos.Split; import io.split.client.dtos.SplitChange; import io.split.client.dtos.Status; +import io.split.client.utils.InputStreamProvider; import io.split.client.utils.LocalhostConstants; import io.split.engine.common.FetchOptions; import io.split.engine.experiments.SplitChangeFetcher; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yaml.snakeyaml.Yaml; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -23,21 +22,20 @@ import static io.split.client.utils.LocalhostSanitizer.createCondition; - public class YamlLocalhostSplitChangeFetcher implements SplitChangeFetcher { private static final Logger _log = LoggerFactory.getLogger(YamlLocalhostSplitChangeFetcher.class); - private final File _splitFile; + private final InputStreamProvider _inputStreamProvider; - public YamlLocalhostSplitChangeFetcher(String filePath) { - _splitFile = new File(filePath); + public YamlLocalhostSplitChangeFetcher(InputStreamProvider inputStreamProvider) { + _inputStreamProvider = inputStreamProvider; } @Override public SplitChange fetch(long since, FetchOptions options) { try { Yaml yaml = new Yaml(); - List>> yamlSplits = yaml.load(new FileReader(_splitFile)); + List>> yamlSplits = yaml.load(_inputStreamProvider.get()); SplitChange splitChange = new SplitChange(); splitChange.splits = new ArrayList<>(); for(Map> aSplit : yamlSplits) { @@ -76,17 +74,8 @@ public SplitChange fetch(long since, FetchOptions options) { splitChange.till = since; splitChange.since = since; return splitChange; - } catch (FileNotFoundException f) { - _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", - _splitFile.getPath(), _splitFile.getPath()), f); - throw new IllegalStateException("Problem fetching splitChanges: " + f.getMessage(), f); } catch (Exception e) { - _log.warn(String.format("Problem to fetch split change using the file %s", - _splitFile.getPath()), e); - throw new IllegalStateException("Problem fetching splitChanges: " + e.getMessage(), e); + throw new IllegalStateException("Problem fetching splitChanges using a yaml file: " + e.getMessage(), e); } } } \ No newline at end of file diff --git a/client/src/main/java/io/split/client/exceptions/InputStreamProviderException.java b/client/src/main/java/io/split/client/exceptions/InputStreamProviderException.java new file mode 100644 index 000000000..33fba556f --- /dev/null +++ b/client/src/main/java/io/split/client/exceptions/InputStreamProviderException.java @@ -0,0 +1,8 @@ +package io.split.client.exceptions; + +public class InputStreamProviderException extends Exception { + + public InputStreamProviderException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/client/src/main/java/io/split/client/utils/FileInputStreamProvider.java b/client/src/main/java/io/split/client/utils/FileInputStreamProvider.java new file mode 100644 index 000000000..c1d9f9812 --- /dev/null +++ b/client/src/main/java/io/split/client/utils/FileInputStreamProvider.java @@ -0,0 +1,26 @@ +package io.split.client.utils; + +import io.split.client.exceptions.InputStreamProviderException; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; + +public class FileInputStreamProvider implements InputStreamProvider { + + private final String _fileName; + + public FileInputStreamProvider(String fileName) { + _fileName = fileName; + } + + @Override + public InputStream get() throws InputStreamProviderException { + try { + return new FileInputStream(_fileName); + } catch (FileNotFoundException f) { + throw new InputStreamProviderException(String.format("Problem fetching splitChanges using file named %s: %s", + _fileName, f.getMessage())); + } + } +} \ No newline at end of file diff --git a/client/src/main/java/io/split/client/utils/FileTypeEnum.java b/client/src/main/java/io/split/client/utils/FileTypeEnum.java new file mode 100644 index 000000000..8084541cb --- /dev/null +++ b/client/src/main/java/io/split/client/utils/FileTypeEnum.java @@ -0,0 +1,7 @@ +package io.split.client.utils; + +public enum FileTypeEnum { + LEGACY, + YAML, + JSON +} diff --git a/client/src/main/java/io/split/client/utils/InputStreamProvider.java b/client/src/main/java/io/split/client/utils/InputStreamProvider.java new file mode 100644 index 000000000..02df28398 --- /dev/null +++ b/client/src/main/java/io/split/client/utils/InputStreamProvider.java @@ -0,0 +1,10 @@ +package io.split.client.utils; + +import io.split.client.exceptions.InputStreamProviderException; + +import java.io.InputStream; + +public interface InputStreamProvider { + + InputStream get() throws InputStreamProviderException; +} diff --git a/client/src/main/java/io/split/client/utils/StaticContentInputStreamProvider.java b/client/src/main/java/io/split/client/utils/StaticContentInputStreamProvider.java new file mode 100644 index 000000000..d129b77c8 --- /dev/null +++ b/client/src/main/java/io/split/client/utils/StaticContentInputStreamProvider.java @@ -0,0 +1,26 @@ +package io.split.client.utils; + +import io.split.client.exceptions.InputStreamProviderException; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.stream.Collectors; + +public class StaticContentInputStreamProvider implements InputStreamProvider { + + private final String _streamContents; + + public StaticContentInputStreamProvider(InputStream inputStream){ + _streamContents = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)) + .lines() + .collect(Collectors.joining("\n")); + } + + @Override + public InputStream get() throws InputStreamProviderException { + return new ByteArrayInputStream(_streamContents.getBytes()); + } +} diff --git a/client/src/test/java/io/split/client/JsonLocalhostSplitChangeFetcherTest.java b/client/src/test/java/io/split/client/JsonLocalhostSplitChangeFetcherTest.java index 164da7740..c355734aa 100644 --- a/client/src/test/java/io/split/client/JsonLocalhostSplitChangeFetcherTest.java +++ b/client/src/test/java/io/split/client/JsonLocalhostSplitChangeFetcherTest.java @@ -4,6 +4,9 @@ import io.split.client.dtos.Split; import io.split.client.dtos.SplitChange; import io.split.client.dtos.Status; +import io.split.client.utils.FileInputStreamProvider; +import io.split.client.utils.InputStreamProvider; +import io.split.client.utils.StaticContentInputStreamProvider; import io.split.engine.common.FetchOptions; import org.junit.Assert; import org.junit.Rule; @@ -12,7 +15,10 @@ import org.mockito.Mockito; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; import java.util.List; import java.util.Optional; @@ -27,8 +33,10 @@ public class JsonLocalhostSplitChangeFetcherTest { private String TEST_4 = "{\"splits\":[{\"trafficTypeName\":\"user\",\"name\":\"SPLIT_1\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-1780071202,\"seed\":-1442762199,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1675443537882,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"user\",\"attribute\":null},\"matcherType\":\"ALL_KEYS\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":100}],\"label\":\"default rule\"}]}],\"since\":-1,\"till\":445345}"; private String TEST_5 = "{\"splits\":[{\"trafficTypeName\":\"user\",\"name\":\"SPLIT_1\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-1780071202,\"seed\":-1442762199,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1675443537882,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"user\",\"attribute\":null},\"matcherType\":\"ALL_KEYS\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":100}],\"label\":\"default rule\"}]},{\"trafficTypeName\":\"user\",\"name\":\"SPLIT_2\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-1780071202,\"seed\":-1442762199,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1675443537882,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"user\",\"attribute\":null},\"matcherType\":\"ALL_KEYS\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":100}],\"label\":\"default rule\"}]}],\"since\":-1,\"till\":-1}"; @Test - public void testParseSplitChange(){ - JsonLocalhostSplitChangeFetcher localhostSplitChangeFetcher = new JsonLocalhostSplitChangeFetcher("src/test/resources/split_init.json"); + public void testParseSplitChange() throws FileNotFoundException { + InputStream inputStream = new FileInputStream("src/test/resources/split_init.json"); + InputStreamProvider inputStreamProvider = new StaticContentInputStreamProvider(inputStream); + JsonLocalhostSplitChangeFetcher localhostSplitChangeFetcher = new JsonLocalhostSplitChangeFetcher(inputStreamProvider); FetchOptions fetchOptions = Mockito.mock(FetchOptions.class); SplitChange splitChange = localhostSplitChangeFetcher.fetch(-1L, fetchOptions); @@ -40,8 +48,10 @@ public void testParseSplitChange(){ } @Test - public void testSinceAndTillSanitization(){ - JsonLocalhostSplitChangeFetcher localhostSplitChangeFetcher = new JsonLocalhostSplitChangeFetcher("src/test/resources/sanitizer/splitChangeTillSanitization.json"); + public void testSinceAndTillSanitization() throws FileNotFoundException { + InputStream inputStream = new FileInputStream("src/test/resources/sanitizer/splitChangeTillSanitization.json"); + InputStreamProvider inputStreamProvider = new StaticContentInputStreamProvider(inputStream); + JsonLocalhostSplitChangeFetcher localhostSplitChangeFetcher = new JsonLocalhostSplitChangeFetcher(inputStreamProvider); FetchOptions fetchOptions = Mockito.mock(FetchOptions.class); SplitChange splitChange = localhostSplitChangeFetcher.fetch(-1L, fetchOptions); @@ -51,8 +61,10 @@ public void testSinceAndTillSanitization(){ } @Test - public void testSplitChangeWithoutSplits(){ - JsonLocalhostSplitChangeFetcher localhostSplitChangeFetcher = new JsonLocalhostSplitChangeFetcher("src/test/resources/sanitizer/splitChangeWithoutSplits.json"); + public void testSplitChangeWithoutSplits() throws FileNotFoundException { + InputStream inputStream = new FileInputStream("src/test/resources/sanitizer/splitChangeWithoutSplits.json"); + InputStreamProvider inputStreamProvider = new StaticContentInputStreamProvider(inputStream); + JsonLocalhostSplitChangeFetcher localhostSplitChangeFetcher = new JsonLocalhostSplitChangeFetcher(inputStreamProvider); FetchOptions fetchOptions = Mockito.mock(FetchOptions.class); SplitChange splitChange = localhostSplitChangeFetcher.fetch(-1L, fetchOptions); @@ -61,8 +73,10 @@ public void testSplitChangeWithoutSplits(){ } @Test - public void testSplitChangeSplitsToSanitize(){ - JsonLocalhostSplitChangeFetcher localhostSplitChangeFetcher = new JsonLocalhostSplitChangeFetcher("src/test/resources/sanitizer/splitChangeSplitsToSanitize.json"); + public void testSplitChangeSplitsToSanitize() throws FileNotFoundException { + InputStream inputStream = new FileInputStream("src/test/resources/sanitizer/splitChangeSplitsToSanitize.json"); + InputStreamProvider inputStreamProvider = new StaticContentInputStreamProvider(inputStream); + JsonLocalhostSplitChangeFetcher localhostSplitChangeFetcher = new JsonLocalhostSplitChangeFetcher(inputStreamProvider); FetchOptions fetchOptions = Mockito.mock(FetchOptions.class); SplitChange splitChange = localhostSplitChangeFetcher.fetch(-1L, fetchOptions); @@ -76,8 +90,10 @@ public void testSplitChangeSplitsToSanitize(){ } @Test - public void testSplitChangeSplitsToSanitizeMatchersNull(){ - JsonLocalhostSplitChangeFetcher localhostSplitChangeFetcher = new JsonLocalhostSplitChangeFetcher("src/test/resources/sanitizer/splitChangerMatchersNull.json"); + public void testSplitChangeSplitsToSanitizeMatchersNull() throws FileNotFoundException { + InputStream inputStream = new FileInputStream("src/test/resources/sanitizer/splitChangerMatchersNull.json"); + InputStreamProvider inputStreamProvider = new StaticContentInputStreamProvider(inputStream); + JsonLocalhostSplitChangeFetcher localhostSplitChangeFetcher = new JsonLocalhostSplitChangeFetcher(inputStreamProvider); FetchOptions fetchOptions = Mockito.mock(FetchOptions.class); SplitChange splitChange = localhostSplitChangeFetcher.fetch(-1L, fetchOptions); @@ -98,7 +114,8 @@ public void testSplitChangeSplitsDifferentScenarios() throws IOException { byte[] test = TEST_0.getBytes(); com.google.common.io.Files.write(test, file); - JsonLocalhostSplitChangeFetcher localhostSplitChangeFetcher = new JsonLocalhostSplitChangeFetcher(file.getAbsolutePath()); + InputStreamProvider inputStreamProvider = new FileInputStreamProvider(file.getAbsolutePath()); + JsonLocalhostSplitChangeFetcher localhostSplitChangeFetcher = new JsonLocalhostSplitChangeFetcher(inputStreamProvider); FetchOptions fetchOptions = Mockito.mock(FetchOptions.class); // 0) The CN from storage is -1, till and since are -1, and sha doesn't exist in the hash. It's going to return a split change with updates. @@ -152,4 +169,13 @@ public void testSplitChangeSplitsDifferentScenarios() throws IOException { Assert.assertEquals(2323, splitChange.till); Assert.assertEquals(2323, splitChange.since); } + + @Test(expected = IllegalStateException.class) + public void processTestForException() { + InputStreamProvider inputStreamProvider = new FileInputStreamProvider("src/test/resources/notExist.json"); + JsonLocalhostSplitChangeFetcher localhostSplitChangeFetcher = new JsonLocalhostSplitChangeFetcher(inputStreamProvider); + FetchOptions fetchOptions = Mockito.mock(FetchOptions.class); + + SplitChange splitChange = localhostSplitChangeFetcher.fetch(-1L, fetchOptions); + } } \ No newline at end of file diff --git a/client/src/test/java/io/split/client/SplitFactoryImplTest.java b/client/src/test/java/io/split/client/SplitFactoryImplTest.java index ad2334d39..f51ccae36 100644 --- a/client/src/test/java/io/split/client/SplitFactoryImplTest.java +++ b/client/src/test/java/io/split/client/SplitFactoryImplTest.java @@ -1,17 +1,24 @@ package io.split.client; import io.split.client.impressions.ImpressionsManager; +import io.split.client.utils.FileTypeEnum; import io.split.integrations.IntegrationsConfig; import io.split.storages.enums.OperationMode; import io.split.storages.pluggable.domain.UserStorageWrapper; import io.split.telemetry.storage.TelemetryStorage; import io.split.telemetry.synchronizer.TelemetrySynchronizer; import junit.framework.TestCase; +import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; import pluggable.CustomStorageWrapper; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URISyntaxException; @@ -162,8 +169,6 @@ public void testFactoryConsumerInstantiation() throws Exception { Mockito.verify(telemetrySynchronizer, Mockito.times(1)).synchronizeConfig(Mockito.anyObject(), Mockito.anyLong(), Mockito.anyObject(), Mockito.anyObject()); } - - @Test public void testFactoryConsumerInstantiationRetryReadiness() throws Exception { CustomStorageWrapper customStorageWrapper = Mockito.mock(CustomStorageWrapper.class); @@ -221,4 +226,122 @@ public void testFactoryConsumerDestroy() throws NoSuchFieldException, URISyntaxE assertTrue(splitFactory.isDestroyed()); Mockito.verify(userStorageWrapper, Mockito.times(1)).disconnect(); } -} + + @Test + public void testLocalhostLegacy() throws URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + SplitClientConfig splitClientConfig = SplitClientConfig.builder() + .setBlockUntilReadyTimeout(10000) + .build(); + SplitFactoryImpl splitFactory = new SplitFactoryImpl("localhost", splitClientConfig); + + Method method = SplitFactoryImpl.class.getDeclaredMethod("createSplitChangeFetcher", SplitClientConfig.class); + method.setAccessible(true); + Object splitChangeFetcher = method.invoke(splitFactory, splitClientConfig); + Assert.assertTrue(splitChangeFetcher instanceof LegacyLocalhostSplitChangeFetcher); + } + + @Test + public void testLocalhostYaml() throws URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + SplitClientConfig splitClientConfig = SplitClientConfig.builder() + .splitFile("src/test/resources/split.yaml") + .setBlockUntilReadyTimeout(10000) + .build(); + SplitFactoryImpl splitFactory = new SplitFactoryImpl("localhost", splitClientConfig); + + Method method = SplitFactoryImpl.class.getDeclaredMethod("createSplitChangeFetcher", SplitClientConfig.class); + method.setAccessible(true); + Object splitChangeFetcher = method.invoke(splitFactory, splitClientConfig); + Assert.assertTrue(splitChangeFetcher instanceof YamlLocalhostSplitChangeFetcher); + } + + @Test + public void testLocalhosJson() throws URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + SplitClientConfig splitClientConfig = SplitClientConfig.builder() + .splitFile("src/test/resources/split_init.json") + .setBlockUntilReadyTimeout(10000) + .build(); + SplitFactoryImpl splitFactory = new SplitFactoryImpl("localhost", splitClientConfig); + + Method method = SplitFactoryImpl.class.getDeclaredMethod("createSplitChangeFetcher", SplitClientConfig.class); + method.setAccessible(true); + Object splitChangeFetcher = method.invoke(splitFactory, splitClientConfig); + Assert.assertTrue(splitChangeFetcher instanceof JsonLocalhostSplitChangeFetcher); + } + + @Test + public void testLocalhosYamlInputStream() throws URISyntaxException, NoSuchMethodException, InvocationTargetException, + IllegalAccessException, FileNotFoundException { + InputStream inputStream = new FileInputStream("src/test/resources/split.yaml"); + SplitClientConfig splitClientConfig = SplitClientConfig.builder() + .splitFile(inputStream, FileTypeEnum.YAML) + .setBlockUntilReadyTimeout(10000) + .build(); + SplitFactoryImpl splitFactory = new SplitFactoryImpl("localhost", splitClientConfig); + + Method method = SplitFactoryImpl.class.getDeclaredMethod("createSplitChangeFetcher", SplitClientConfig.class); + method.setAccessible(true); + Object splitChangeFetcher = method.invoke(splitFactory, splitClientConfig); + Assert.assertTrue(splitChangeFetcher instanceof YamlLocalhostSplitChangeFetcher); + } + + @Test + public void testLocalhosJsonInputStream() throws URISyntaxException, NoSuchMethodException, InvocationTargetException, + IllegalAccessException, FileNotFoundException { + InputStream inputStream = new FileInputStream("src/test/resources/split_init.json"); + SplitClientConfig splitClientConfig = SplitClientConfig.builder() + .splitFile(inputStream, FileTypeEnum.JSON) + .setBlockUntilReadyTimeout(10000) + .build(); + SplitFactoryImpl splitFactory = new SplitFactoryImpl("localhost", splitClientConfig); + + Method method = SplitFactoryImpl.class.getDeclaredMethod("createSplitChangeFetcher", SplitClientConfig.class); + method.setAccessible(true); + Object splitChangeFetcher = method.invoke(splitFactory, splitClientConfig); + Assert.assertTrue(splitChangeFetcher instanceof JsonLocalhostSplitChangeFetcher); + } + + @Test + public void testLocalhosJsonInputStreamNull() throws URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + SplitClientConfig splitClientConfig = SplitClientConfig.builder() + .splitFile(null, FileTypeEnum.JSON) + .setBlockUntilReadyTimeout(10000) + .build(); + SplitFactoryImpl splitFactory = new SplitFactoryImpl("localhost", splitClientConfig); + + Method method = SplitFactoryImpl.class.getDeclaredMethod("createSplitChangeFetcher", SplitClientConfig.class); + method.setAccessible(true); + Object splitChangeFetcher = method.invoke(splitFactory, splitClientConfig); + Assert.assertTrue(splitChangeFetcher instanceof LegacyLocalhostSplitChangeFetcher); + } + + @Test + public void testLocalhosJsonInputStreamAndFileTypeNull() throws URISyntaxException, NoSuchMethodException, InvocationTargetException, + IllegalAccessException, FileNotFoundException { + InputStream inputStream = new FileInputStream("src/test/resources/split_init.json"); + SplitClientConfig splitClientConfig = SplitClientConfig.builder() + .splitFile(inputStream, null) + .setBlockUntilReadyTimeout(10000) + .build(); + SplitFactoryImpl splitFactory = new SplitFactoryImpl("localhost", splitClientConfig); + + Method method = SplitFactoryImpl.class.getDeclaredMethod("createSplitChangeFetcher", SplitClientConfig.class); + method.setAccessible(true); + Object splitChangeFetcher = method.invoke(splitFactory, splitClientConfig); + Assert.assertTrue(splitChangeFetcher instanceof LegacyLocalhostSplitChangeFetcher); + } + + @Test + public void testLocalhosJsonInputStreamNullAndFileTypeNull() throws URISyntaxException, NoSuchMethodException, InvocationTargetException, + IllegalAccessException { + SplitClientConfig splitClientConfig = SplitClientConfig.builder() + .splitFile(null, null) + .setBlockUntilReadyTimeout(10000) + .build(); + SplitFactoryImpl splitFactory = new SplitFactoryImpl("localhost", splitClientConfig); + + Method method = SplitFactoryImpl.class.getDeclaredMethod("createSplitChangeFetcher", SplitClientConfig.class); + method.setAccessible(true); + Object splitChangeFetcher = method.invoke(splitFactory, splitClientConfig); + Assert.assertTrue(splitChangeFetcher instanceof LegacyLocalhostSplitChangeFetcher); + } +} \ No newline at end of file diff --git a/client/src/test/java/io/split/client/YamlLocalhostSplitChangeFetcherTest.java b/client/src/test/java/io/split/client/YamlLocalhostSplitChangeFetcherTest.java index 4f89012ef..dabd96781 100644 --- a/client/src/test/java/io/split/client/YamlLocalhostSplitChangeFetcherTest.java +++ b/client/src/test/java/io/split/client/YamlLocalhostSplitChangeFetcherTest.java @@ -2,6 +2,8 @@ import io.split.client.dtos.Split; import io.split.client.dtos.SplitChange; +import io.split.client.utils.FileInputStreamProvider; +import io.split.client.utils.InputStreamProvider; import io.split.client.utils.LocalhostUtils; import io.split.engine.common.FetchOptions; import org.junit.Assert; @@ -58,7 +60,8 @@ public void testParseSplitChange() throws IOException { yaml.dump(allSplits, writer); LocalhostUtils.writeFile(file, writer); - YamlLocalhostSplitChangeFetcher localhostSplitChangeFetcher = new YamlLocalhostSplitChangeFetcher(file.getAbsolutePath()); + InputStreamProvider inputStreamProvider = new FileInputStreamProvider(file.getAbsolutePath()); + YamlLocalhostSplitChangeFetcher localhostSplitChangeFetcher = new YamlLocalhostSplitChangeFetcher(inputStreamProvider); FetchOptions fetchOptions = Mockito.mock(FetchOptions.class); SplitChange splitChange = localhostSplitChangeFetcher.fetch(-1L, fetchOptions); @@ -71,4 +74,13 @@ public void testParseSplitChange() throws IOException { Assert.assertEquals("control", split.defaultTreatment); } } + + @Test(expected = IllegalStateException.class) + public void processTestForException() { + InputStreamProvider inputStreamProvider = new FileInputStreamProvider("src/test/resources/notExist.yaml"); + YamlLocalhostSplitChangeFetcher localhostSplitChangeFetcher = new YamlLocalhostSplitChangeFetcher(inputStreamProvider); + FetchOptions fetchOptions = Mockito.mock(FetchOptions.class); + + SplitChange splitChange = localhostSplitChangeFetcher.fetch(-1L, fetchOptions); + } } \ No newline at end of file diff --git a/client/src/test/java/io/split/client/utils/FileInputStreamProviderTest.java b/client/src/test/java/io/split/client/utils/FileInputStreamProviderTest.java new file mode 100644 index 000000000..5c9e34d2d --- /dev/null +++ b/client/src/test/java/io/split/client/utils/FileInputStreamProviderTest.java @@ -0,0 +1,13 @@ +package io.split.client.utils; + +import io.split.client.exceptions.InputStreamProviderException; +import org.junit.Test; + +public class FileInputStreamProviderTest { + + @Test(expected = InputStreamProviderException.class) + public void processTestForException() throws InputStreamProviderException { + FileInputStreamProvider fileInputStreamProvider = new FileInputStreamProvider("src/test/resources/notExist.json"); + fileInputStreamProvider.get(); + } +} \ No newline at end of file diff --git a/client/src/test/java/io/split/engine/common/LocalhostSynchronizerTest.java b/client/src/test/java/io/split/engine/common/LocalhostSynchronizerTest.java index 3d303845e..4981cd6cc 100644 --- a/client/src/test/java/io/split/engine/common/LocalhostSynchronizerTest.java +++ b/client/src/test/java/io/split/engine/common/LocalhostSynchronizerTest.java @@ -2,6 +2,8 @@ import io.split.client.LocalhostSegmentChangeFetcher; import io.split.client.JsonLocalhostSplitChangeFetcher; +import io.split.client.utils.FileInputStreamProvider; +import io.split.client.utils.InputStreamProvider; import io.split.engine.experiments.SplitChangeFetcher; import io.split.engine.experiments.SplitFetcher; import io.split.engine.experiments.SplitFetcherImp; @@ -25,10 +27,11 @@ public class LocalhostSynchronizerTest { private static final TelemetryStorage TELEMETRY_STORAGE_NOOP = Mockito.mock(NoopTelemetryStorage.class); @Test - public void testSyncAll() { + public void testSyncAll(){ SplitCache splitCacheProducer = new InMemoryCacheImp(); - SplitChangeFetcher splitChangeFetcher = new JsonLocalhostSplitChangeFetcher("src/test/resources/split_init.json"); + InputStreamProvider inputStreamProvider = new FileInputStreamProvider("src/test/resources/split_init.json"); + SplitChangeFetcher splitChangeFetcher = new JsonLocalhostSplitChangeFetcher(inputStreamProvider); SplitParser splitParser = new SplitParser(); SplitFetcher splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheProducer, TELEMETRY_STORAGE_NOOP); diff --git a/client/src/test/java/io/split/engine/experiments/SplitFetcherImpTest.java b/client/src/test/java/io/split/engine/experiments/SplitFetcherImpTest.java index d838cc0de..643ce86b8 100644 --- a/client/src/test/java/io/split/engine/experiments/SplitFetcherImpTest.java +++ b/client/src/test/java/io/split/engine/experiments/SplitFetcherImpTest.java @@ -1,6 +1,8 @@ package io.split.engine.experiments; import io.split.client.JsonLocalhostSplitChangeFetcher; +import io.split.client.utils.FileInputStreamProvider; +import io.split.client.utils.InputStreamProvider; import io.split.engine.common.FetchOptions; import io.split.storages.SplitCacheProducer; import io.split.storages.memory.InMemoryCacheImp; @@ -15,10 +17,11 @@ public class SplitFetcherImpTest { private static final TelemetryStorage TELEMETRY_STORAGE_NOOP = Mockito.mock(NoopTelemetryStorage.class); @Test - public void testLocalHost(){ + public void testLocalHost() { SplitCacheProducer splitCacheProducer = new InMemoryCacheImp(); - SplitChangeFetcher splitChangeFetcher = new JsonLocalhostSplitChangeFetcher("src/test/resources/split_init.json"); + InputStreamProvider inputStreamProvider = new FileInputStreamProvider("src/test/resources/split_init.json"); + SplitChangeFetcher splitChangeFetcher = new JsonLocalhostSplitChangeFetcher(inputStreamProvider); SplitParser splitParser = new SplitParser(); FetchOptions fetchOptions = new FetchOptions.Builder().build(); SplitFetcher splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheProducer, TELEMETRY_STORAGE_NOOP); diff --git a/client/src/test/java/io/split/engine/segments/SegmentSynchronizationTaskImpTest.java b/client/src/test/java/io/split/engine/segments/SegmentSynchronizationTaskImpTest.java index f9e1e354d..32ba6fa0b 100644 --- a/client/src/test/java/io/split/engine/segments/SegmentSynchronizationTaskImpTest.java +++ b/client/src/test/java/io/split/engine/segments/SegmentSynchronizationTaskImpTest.java @@ -3,6 +3,8 @@ import com.google.common.collect.Maps; import io.split.client.LocalhostSegmentChangeFetcher; import io.split.client.JsonLocalhostSplitChangeFetcher; +import io.split.client.utils.InputStreamProvider; +import io.split.client.utils.StaticContentInputStreamProvider; import io.split.engine.common.FetchOptions; import io.split.engine.experiments.SplitChangeFetcher; import io.split.engine.experiments.SplitFetcher; @@ -24,6 +26,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.List; @@ -145,11 +150,13 @@ public void testFetchAllAsynchronousAndGetTrue() throws NoSuchFieldException, Il } @Test - public void testLocalhostSegmentChangeFetcher() throws InterruptedException { + public void testLocalhostSegmentChangeFetcher() throws InterruptedException, FileNotFoundException { SplitCache splitCacheProducer = new InMemoryCacheImp(); - SplitChangeFetcher splitChangeFetcher = new JsonLocalhostSplitChangeFetcher("src/test/resources/split_init.json"); + InputStream inputStream = new FileInputStream("src/test/resources/split_init.json"); + InputStreamProvider inputStreamProvider = new StaticContentInputStreamProvider(inputStream); + SplitChangeFetcher splitChangeFetcher = new JsonLocalhostSplitChangeFetcher(inputStreamProvider); SplitParser splitParser = new SplitParser(); FetchOptions fetchOptions = new FetchOptions.Builder().build(); SplitFetcher splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheProducer, TELEMETRY_STORAGE_NOOP); diff --git a/pluggable-storage/pom.xml b/pluggable-storage/pom.xml index f2a69fc6a..290fe939d 100644 --- a/pluggable-storage/pom.xml +++ b/pluggable-storage/pom.xml @@ -6,7 +6,7 @@ java-client-parent io.split.client - 4.8.1 + 4.9.0 2.0.0 diff --git a/pom.xml b/pom.xml index 9b4160086..4536b6a2a 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 io.split.client java-client-parent - 4.8.1 + 4.9.0 diff --git a/redis-wrapper/pom.xml b/redis-wrapper/pom.xml index 99b77073e..c733a21ab 100644 --- a/redis-wrapper/pom.xml +++ b/redis-wrapper/pom.xml @@ -6,7 +6,7 @@ java-client-parent io.split.client - 4.8.1 + 4.9.0 redis-wrapper 3.0.1 diff --git a/testing/pom.xml b/testing/pom.xml index d35996d06..11160430e 100644 --- a/testing/pom.xml +++ b/testing/pom.xml @@ -5,7 +5,7 @@ io.split.client java-client-parent - 4.8.1 + 4.9.0 java-client-testing jar