From ba303d811a93eff5dadd83d6932a7de2d84a8316 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Tue, 25 Feb 2025 15:36:51 -0300 Subject: [PATCH 01/26] Persistent storage --- .../storage/rbs/RuleBasedSegmentSnapshot.java | 12 +- .../rbs/RuleBasedSegmentStorageImpl.java | 9 +- ...PersistentRuleBasedSegmentStorageImpl.java | 161 ++++++++++++++++++ .../rbs/RuleBasedSegmentStorageImplTest.java | 5 +- 4 files changed, 175 insertions(+), 12 deletions(-) create mode 100644 src/main/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageImpl.java diff --git a/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentSnapshot.java b/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentSnapshot.java index fdb618bcd..d62e31a42 100644 --- a/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentSnapshot.java +++ b/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentSnapshot.java @@ -1,21 +1,23 @@ package io.split.android.client.storage.rbs; -import java.util.Set; +import static io.split.android.client.utils.Utils.checkNotNull; + +import java.util.Map; import io.split.android.client.dtos.RuleBasedSegment; public class RuleBasedSegmentSnapshot { - private final Set mSegments; + private final Map mSegments; private final long mChangeNumber; - public RuleBasedSegmentSnapshot(Set segments, long changeNumber) { - mSegments = segments; + public RuleBasedSegmentSnapshot(Map segments, long changeNumber) { + mSegments = checkNotNull(segments); mChangeNumber = changeNumber; } - public Set getSegments() { + public Map getSegments() { return mSegments; } diff --git a/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImpl.java b/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImpl.java index 013092227..80b5759b3 100644 --- a/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImpl.java +++ b/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImpl.java @@ -6,6 +6,7 @@ import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; +import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -77,13 +78,11 @@ public boolean contains(@NonNull Set segmentNames) { @WorkerThread @Override - public void loadLocal() { + public synchronized void loadLocal() { RuleBasedSegmentSnapshot snapshot = mPersistentStorage.getSnapshot(); - Set segments = snapshot.getSegments(); + Map segments = snapshot.getSegments(); mChangeNumber = snapshot.getChangeNumber(); - for (RuleBasedSegment segment : segments) { - mInMemorySegments.put(segment.getName(), segment); - } + mInMemorySegments.putAll(segments); } @WorkerThread diff --git a/src/main/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageImpl.java b/src/main/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageImpl.java new file mode 100644 index 000000000..baf4c872d --- /dev/null +++ b/src/main/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageImpl.java @@ -0,0 +1,161 @@ +package io.split.android.client.storage.rbs; + +import static io.split.android.client.utils.Utils.checkNotNull; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; + +import io.split.android.client.dtos.RuleBasedSegment; +import io.split.android.client.storage.cipher.SplitCipher; +import io.split.android.client.storage.db.SplitRoomDatabase; +import io.split.android.client.storage.db.rbs.RuleBasedSegmentDao; +import io.split.android.client.storage.db.rbs.RuleBasedSegmentEntity; +import io.split.android.client.storage.general.GeneralInfoStorage; +import io.split.android.client.utils.Json; +import io.split.android.client.utils.logger.Logger; + +class SqLitePersistentRuleBasedSegmentStorageImpl implements PersistentRuleBasedSegmentStorage { + + private final RuleBasedSegmentDao mDao; + private final SplitRoomDatabase mDatabase; + private final GeneralInfoStorage mGeneralInfoStorage; + private final SplitCipher mCipher; + + public SqLitePersistentRuleBasedSegmentStorageImpl(SplitCipher cipher, + SplitRoomDatabase database, + GeneralInfoStorage generalInfoStorage) { + mCipher = checkNotNull(cipher); + mDatabase = checkNotNull(database); + mDao = database.ruleBasedSegmentDao(); + mGeneralInfoStorage = checkNotNull(generalInfoStorage); + } + + @Override + public RuleBasedSegmentSnapshot getSnapshot() { + return mDatabase.runInTransaction(new SnapshotLoader(mDao, mCipher, mGeneralInfoStorage)); + } + + @Override + public void update(Set toAdd, Set toRemove, long changeNumber) { + mDatabase.runInTransaction(new Updater(mCipher, mDao, mGeneralInfoStorage, toAdd, toRemove, changeNumber)); + } + + @Override + public void clear() { + mDatabase.runInTransaction(new Runnable() { + @Override + public void run() { + try { + mDao.deleteAll(); + mGeneralInfoStorage.setRbsChangeNumber(-1); + } catch (Exception e) { + Logger.e("Error clearing RBS: " + e.getLocalizedMessage()); + throw e; + } + } + }); + } + + static final class SnapshotLoader implements Callable { + + private final RuleBasedSegmentDao mDao; + private final SplitCipher mCipher; + private final GeneralInfoStorage mGeneralInfoStorage; + + public SnapshotLoader(RuleBasedSegmentDao dao, SplitCipher cipher, GeneralInfoStorage generalInfoStorage) { + mDao = checkNotNull(dao); + mCipher = checkNotNull(cipher); + mGeneralInfoStorage = checkNotNull(generalInfoStorage); + } + + @Override + public RuleBasedSegmentSnapshot call() { + try { + long changeNumber = mGeneralInfoStorage.getFlagsChangeNumber(); + List entities = mDao.getAll(); + Map segments = convertToDTOs(entities); + + return new RuleBasedSegmentSnapshot(segments, changeNumber); + } catch (Exception e) { + Logger.e("Error loading RBS from persistent storage", e.getLocalizedMessage()); + throw e; + } + } + + private Map convertToDTOs(List entities) { + Map segments = new HashMap<>(); + if (entities != null) { + for (RuleBasedSegmentEntity entity : entities) { + String name = mCipher.decrypt(entity.getName()); + String body = mCipher.encrypt(entity.getBody()); + if (name == null || body == null) { + continue; + } + + RuleBasedSegment ruleBasedSegment = Json.fromJson(body, RuleBasedSegment.class); + segments.put(name, ruleBasedSegment); + } + } + return segments; + } + } + + static final class Updater implements Runnable { + + @NonNull + private final SplitCipher mCipher; + @NonNull + private final GeneralInfoStorage mGeneralInfoStorage; + @NonNull + private final RuleBasedSegmentDao mDao; + @NonNull + private final Set mToAdd; + @NonNull + private final Set mToRemove; + private final long mChangeNumber; + + public Updater(@NonNull SplitCipher cipher, + @NonNull RuleBasedSegmentDao dao, + @NonNull GeneralInfoStorage generalInfoStorage, + @NonNull Set toAdd, + @NonNull Set toRemove, + long changeNumber) { + mCipher = checkNotNull(cipher); + mDao = checkNotNull(dao); + mGeneralInfoStorage = checkNotNull(generalInfoStorage); + mToAdd = checkNotNull(toAdd); + mToRemove = checkNotNull(toRemove); + mChangeNumber = changeNumber; + } + + @Override + public void run() { + try { + List toDelete = new ArrayList<>(); + for (RuleBasedSegment segment : mToRemove) { + toDelete.add(mCipher.encrypt(segment.getName())); + } + + List toAdd = new ArrayList<>(); + for (RuleBasedSegment segment : mToAdd) { + String name = mCipher.encrypt(segment.getName()); + String body = mCipher.encrypt(Json.toJson(segment)); + toAdd.add(new RuleBasedSegmentEntity(name, body, System.currentTimeMillis())); + } + + mDao.delete(toDelete); + mDao.insert(toAdd); + mGeneralInfoStorage.setRbsChangeNumber(mChangeNumber); + } catch (Exception e) { + Logger.e("Error updating RBS: " + e.getLocalizedMessage()); + throw e; + } + } + } +} diff --git a/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java b/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java index 5af577c6a..4596bf921 100644 --- a/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java +++ b/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java @@ -16,6 +16,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.Map; import java.util.Set; import io.split.android.client.dtos.Excluded; @@ -159,7 +160,7 @@ public void updateReturnsTrueWhenThereWereAddedSegments() { @Test public void loadLocalGetsSnapshotFromPersistentStorage() { - when(mPersistentStorage.getSnapshot()).thenReturn(new RuleBasedSegmentSnapshot(Set.of(), 1)); + when(mPersistentStorage.getSnapshot()).thenReturn(new RuleBasedSegmentSnapshot(Map.of(), 1)); storage.loadLocal(); verify(mPersistentStorage).getSnapshot(); @@ -167,7 +168,7 @@ public void loadLocalGetsSnapshotFromPersistentStorage() { @Test public void loadLocalPopulatesValues() { - RuleBasedSegmentSnapshot snapshot = new RuleBasedSegmentSnapshot(Set.of(createRuleBasedSegment("segment1")), + RuleBasedSegmentSnapshot snapshot = new RuleBasedSegmentSnapshot(Map.of("segment1", createRuleBasedSegment("segment1")), 1); when(mPersistentStorage.getSnapshot()).thenReturn(snapshot); From 1ade063b46c99c488ee808d16bdfa9c635d994c9 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Tue, 25 Feb 2025 16:24:51 -0300 Subject: [PATCH 02/26] WIP --- .../android/client/storage/rbs/Clearer.java | 27 +++ .../PersistentRuleBasedSegmentStorage.java | 5 + .../client/storage/rbs/SnapshotLoader.java | 60 +++++++ ...LitePersistentRuleBasedSegmentStorage.java | 43 +++++ ...PersistentRuleBasedSegmentStorageImpl.java | 161 ------------------ ...istentRuleBasedSegmentStorageProvider.java | 19 +++ .../android/client/storage/rbs/Updater.java | 70 ++++++++ ...PersistentRuleBasedSegmentStorageTest.java | 6 + 8 files changed, 230 insertions(+), 161 deletions(-) create mode 100644 src/main/java/io/split/android/client/storage/rbs/Clearer.java create mode 100644 src/main/java/io/split/android/client/storage/rbs/SnapshotLoader.java create mode 100644 src/main/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorage.java delete mode 100644 src/main/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageImpl.java create mode 100644 src/main/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageProvider.java create mode 100644 src/main/java/io/split/android/client/storage/rbs/Updater.java create mode 100644 src/test/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageTest.java diff --git a/src/main/java/io/split/android/client/storage/rbs/Clearer.java b/src/main/java/io/split/android/client/storage/rbs/Clearer.java new file mode 100644 index 000000000..30327f124 --- /dev/null +++ b/src/main/java/io/split/android/client/storage/rbs/Clearer.java @@ -0,0 +1,27 @@ +package io.split.android.client.storage.rbs; + +import io.split.android.client.storage.db.rbs.RuleBasedSegmentDao; +import io.split.android.client.storage.general.GeneralInfoStorage; +import io.split.android.client.utils.logger.Logger; + +class Clearer implements Runnable { + + private final RuleBasedSegmentDao mDao; + private final GeneralInfoStorage mGeneralInfoStorage; + + public Clearer(RuleBasedSegmentDao dao, GeneralInfoStorage generalInfoStorage) { + mDao = dao; + mGeneralInfoStorage = generalInfoStorage; + } + + @Override + public void run() { + try { + mDao.deleteAll(); + mGeneralInfoStorage.setRbsChangeNumber(-1); + } catch (Exception e) { + Logger.e("Error clearing RBS: " + e.getLocalizedMessage()); + throw e; + } + } +} diff --git a/src/main/java/io/split/android/client/storage/rbs/PersistentRuleBasedSegmentStorage.java b/src/main/java/io/split/android/client/storage/rbs/PersistentRuleBasedSegmentStorage.java index c5329bf07..e2bd35634 100644 --- a/src/main/java/io/split/android/client/storage/rbs/PersistentRuleBasedSegmentStorage.java +++ b/src/main/java/io/split/android/client/storage/rbs/PersistentRuleBasedSegmentStorage.java @@ -11,4 +11,9 @@ public interface PersistentRuleBasedSegmentStorage { void update(Set toAdd, Set toRemove, long changeNumber); void clear(); + + interface Provider { + + PersistentRuleBasedSegmentStorage get(); + } } diff --git a/src/main/java/io/split/android/client/storage/rbs/SnapshotLoader.java b/src/main/java/io/split/android/client/storage/rbs/SnapshotLoader.java new file mode 100644 index 000000000..11cea6182 --- /dev/null +++ b/src/main/java/io/split/android/client/storage/rbs/SnapshotLoader.java @@ -0,0 +1,60 @@ +package io.split.android.client.storage.rbs; + +import static io.split.android.client.utils.Utils.checkNotNull; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; + +import io.split.android.client.dtos.RuleBasedSegment; +import io.split.android.client.storage.cipher.SplitCipher; +import io.split.android.client.storage.db.rbs.RuleBasedSegmentDao; +import io.split.android.client.storage.db.rbs.RuleBasedSegmentEntity; +import io.split.android.client.storage.general.GeneralInfoStorage; +import io.split.android.client.utils.Json; +import io.split.android.client.utils.logger.Logger; + +final class SnapshotLoader implements Callable { + + private final RuleBasedSegmentDao mDao; + private final SplitCipher mCipher; + private final GeneralInfoStorage mGeneralInfoStorage; + + SnapshotLoader(RuleBasedSegmentDao dao, SplitCipher cipher, GeneralInfoStorage generalInfoStorage) { + mDao = checkNotNull(dao); + mCipher = checkNotNull(cipher); + mGeneralInfoStorage = checkNotNull(generalInfoStorage); + } + + @Override + public RuleBasedSegmentSnapshot call() { + try { + long changeNumber = mGeneralInfoStorage.getFlagsChangeNumber(); + List entities = mDao.getAll(); + Map segments = convertToDTOs(entities); + + return new RuleBasedSegmentSnapshot(segments, changeNumber); + } catch (Exception e) { + Logger.e("Error loading RBS from persistent storage", e.getLocalizedMessage()); + throw e; + } + } + + private Map convertToDTOs(List entities) { + Map segments = new HashMap<>(); + if (entities != null) { + for (RuleBasedSegmentEntity entity : entities) { + String name = mCipher.decrypt(entity.getName()); + String body = mCipher.encrypt(entity.getBody()); + if (name == null || body == null) { + continue; + } + + RuleBasedSegment ruleBasedSegment = Json.fromJson(body, RuleBasedSegment.class); + segments.put(name, ruleBasedSegment); + } + } + return segments; + } +} diff --git a/src/main/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorage.java b/src/main/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorage.java new file mode 100644 index 000000000..63d09f09b --- /dev/null +++ b/src/main/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorage.java @@ -0,0 +1,43 @@ +package io.split.android.client.storage.rbs; + +import static io.split.android.client.utils.Utils.checkNotNull; + +import java.util.Set; + +import io.split.android.client.dtos.RuleBasedSegment; +import io.split.android.client.storage.cipher.SplitCipher; +import io.split.android.client.storage.db.SplitRoomDatabase; +import io.split.android.client.storage.db.rbs.RuleBasedSegmentDao; +import io.split.android.client.storage.general.GeneralInfoStorage; + +class SqLitePersistentRuleBasedSegmentStorage implements PersistentRuleBasedSegmentStorage { + + private final RuleBasedSegmentDao mDao; + private final SplitRoomDatabase mDatabase; + private final GeneralInfoStorage mGeneralInfoStorage; + private final SplitCipher mCipher; + + public SqLitePersistentRuleBasedSegmentStorage(SplitCipher cipher, + SplitRoomDatabase database, + GeneralInfoStorage generalInfoStorage) { + mCipher = checkNotNull(cipher); + mDatabase = checkNotNull(database); + mDao = database.ruleBasedSegmentDao(); + mGeneralInfoStorage = checkNotNull(generalInfoStorage); + } + + @Override + public RuleBasedSegmentSnapshot getSnapshot() { + return mDatabase.runInTransaction(new SnapshotLoader(mDao, mCipher, mGeneralInfoStorage)); + } + + @Override + public void update(Set toAdd, Set toRemove, long changeNumber) { + mDatabase.runInTransaction(new Updater(mCipher, mDao, mGeneralInfoStorage, toAdd, toRemove, changeNumber)); + } + + @Override + public void clear() { + mDatabase.runInTransaction(new Clearer(mDao, mGeneralInfoStorage)); + } +} diff --git a/src/main/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageImpl.java b/src/main/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageImpl.java deleted file mode 100644 index baf4c872d..000000000 --- a/src/main/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageImpl.java +++ /dev/null @@ -1,161 +0,0 @@ -package io.split.android.client.storage.rbs; - -import static io.split.android.client.utils.Utils.checkNotNull; - -import androidx.annotation.NonNull; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.Callable; - -import io.split.android.client.dtos.RuleBasedSegment; -import io.split.android.client.storage.cipher.SplitCipher; -import io.split.android.client.storage.db.SplitRoomDatabase; -import io.split.android.client.storage.db.rbs.RuleBasedSegmentDao; -import io.split.android.client.storage.db.rbs.RuleBasedSegmentEntity; -import io.split.android.client.storage.general.GeneralInfoStorage; -import io.split.android.client.utils.Json; -import io.split.android.client.utils.logger.Logger; - -class SqLitePersistentRuleBasedSegmentStorageImpl implements PersistentRuleBasedSegmentStorage { - - private final RuleBasedSegmentDao mDao; - private final SplitRoomDatabase mDatabase; - private final GeneralInfoStorage mGeneralInfoStorage; - private final SplitCipher mCipher; - - public SqLitePersistentRuleBasedSegmentStorageImpl(SplitCipher cipher, - SplitRoomDatabase database, - GeneralInfoStorage generalInfoStorage) { - mCipher = checkNotNull(cipher); - mDatabase = checkNotNull(database); - mDao = database.ruleBasedSegmentDao(); - mGeneralInfoStorage = checkNotNull(generalInfoStorage); - } - - @Override - public RuleBasedSegmentSnapshot getSnapshot() { - return mDatabase.runInTransaction(new SnapshotLoader(mDao, mCipher, mGeneralInfoStorage)); - } - - @Override - public void update(Set toAdd, Set toRemove, long changeNumber) { - mDatabase.runInTransaction(new Updater(mCipher, mDao, mGeneralInfoStorage, toAdd, toRemove, changeNumber)); - } - - @Override - public void clear() { - mDatabase.runInTransaction(new Runnable() { - @Override - public void run() { - try { - mDao.deleteAll(); - mGeneralInfoStorage.setRbsChangeNumber(-1); - } catch (Exception e) { - Logger.e("Error clearing RBS: " + e.getLocalizedMessage()); - throw e; - } - } - }); - } - - static final class SnapshotLoader implements Callable { - - private final RuleBasedSegmentDao mDao; - private final SplitCipher mCipher; - private final GeneralInfoStorage mGeneralInfoStorage; - - public SnapshotLoader(RuleBasedSegmentDao dao, SplitCipher cipher, GeneralInfoStorage generalInfoStorage) { - mDao = checkNotNull(dao); - mCipher = checkNotNull(cipher); - mGeneralInfoStorage = checkNotNull(generalInfoStorage); - } - - @Override - public RuleBasedSegmentSnapshot call() { - try { - long changeNumber = mGeneralInfoStorage.getFlagsChangeNumber(); - List entities = mDao.getAll(); - Map segments = convertToDTOs(entities); - - return new RuleBasedSegmentSnapshot(segments, changeNumber); - } catch (Exception e) { - Logger.e("Error loading RBS from persistent storage", e.getLocalizedMessage()); - throw e; - } - } - - private Map convertToDTOs(List entities) { - Map segments = new HashMap<>(); - if (entities != null) { - for (RuleBasedSegmentEntity entity : entities) { - String name = mCipher.decrypt(entity.getName()); - String body = mCipher.encrypt(entity.getBody()); - if (name == null || body == null) { - continue; - } - - RuleBasedSegment ruleBasedSegment = Json.fromJson(body, RuleBasedSegment.class); - segments.put(name, ruleBasedSegment); - } - } - return segments; - } - } - - static final class Updater implements Runnable { - - @NonNull - private final SplitCipher mCipher; - @NonNull - private final GeneralInfoStorage mGeneralInfoStorage; - @NonNull - private final RuleBasedSegmentDao mDao; - @NonNull - private final Set mToAdd; - @NonNull - private final Set mToRemove; - private final long mChangeNumber; - - public Updater(@NonNull SplitCipher cipher, - @NonNull RuleBasedSegmentDao dao, - @NonNull GeneralInfoStorage generalInfoStorage, - @NonNull Set toAdd, - @NonNull Set toRemove, - long changeNumber) { - mCipher = checkNotNull(cipher); - mDao = checkNotNull(dao); - mGeneralInfoStorage = checkNotNull(generalInfoStorage); - mToAdd = checkNotNull(toAdd); - mToRemove = checkNotNull(toRemove); - mChangeNumber = changeNumber; - } - - @Override - public void run() { - try { - List toDelete = new ArrayList<>(); - for (RuleBasedSegment segment : mToRemove) { - toDelete.add(mCipher.encrypt(segment.getName())); - } - - List toAdd = new ArrayList<>(); - for (RuleBasedSegment segment : mToAdd) { - String name = mCipher.encrypt(segment.getName()); - String body = mCipher.encrypt(Json.toJson(segment)); - toAdd.add(new RuleBasedSegmentEntity(name, body, System.currentTimeMillis())); - } - - mDao.delete(toDelete); - mDao.insert(toAdd); - mGeneralInfoStorage.setRbsChangeNumber(mChangeNumber); - } catch (Exception e) { - Logger.e("Error updating RBS: " + e.getLocalizedMessage()); - throw e; - } - } - } -} diff --git a/src/main/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageProvider.java b/src/main/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageProvider.java new file mode 100644 index 000000000..eb5a279b6 --- /dev/null +++ b/src/main/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageProvider.java @@ -0,0 +1,19 @@ +package io.split.android.client.storage.rbs; + +import io.split.android.client.storage.cipher.SplitCipher; +import io.split.android.client.storage.db.SplitRoomDatabase; +import io.split.android.client.storage.general.GeneralInfoStorage; + +public class SqLitePersistentRuleBasedSegmentStorageProvider implements PersistentRuleBasedSegmentStorage.Provider { + + private final SqLitePersistentRuleBasedSegmentStorage mPersistentStorage; + + public SqLitePersistentRuleBasedSegmentStorageProvider(SplitCipher cipher, SplitRoomDatabase database, GeneralInfoStorage generalInfoStorage) { + mPersistentStorage = new SqLitePersistentRuleBasedSegmentStorage(cipher, database, generalInfoStorage); + } + + @Override + public PersistentRuleBasedSegmentStorage get() { + return mPersistentStorage; + } +} diff --git a/src/main/java/io/split/android/client/storage/rbs/Updater.java b/src/main/java/io/split/android/client/storage/rbs/Updater.java new file mode 100644 index 000000000..375d03968 --- /dev/null +++ b/src/main/java/io/split/android/client/storage/rbs/Updater.java @@ -0,0 +1,70 @@ +package io.split.android.client.storage.rbs; + +import static io.split.android.client.utils.Utils.checkNotNull; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import io.split.android.client.dtos.RuleBasedSegment; +import io.split.android.client.storage.cipher.SplitCipher; +import io.split.android.client.storage.db.rbs.RuleBasedSegmentDao; +import io.split.android.client.storage.db.rbs.RuleBasedSegmentEntity; +import io.split.android.client.storage.general.GeneralInfoStorage; +import io.split.android.client.utils.Json; +import io.split.android.client.utils.logger.Logger; + +final class Updater implements Runnable { + + @NonNull + private final SplitCipher mCipher; + @NonNull + private final GeneralInfoStorage mGeneralInfoStorage; + @NonNull + private final RuleBasedSegmentDao mDao; + @NonNull + private final Set mToAdd; + @NonNull + private final Set mToRemove; + private final long mChangeNumber; + + Updater(@NonNull SplitCipher cipher, + @NonNull RuleBasedSegmentDao dao, + @NonNull GeneralInfoStorage generalInfoStorage, + @NonNull Set toAdd, + @NonNull Set toRemove, + long changeNumber) { + mCipher = checkNotNull(cipher); + mDao = checkNotNull(dao); + mGeneralInfoStorage = checkNotNull(generalInfoStorage); + mToAdd = checkNotNull(toAdd); + mToRemove = checkNotNull(toRemove); + mChangeNumber = changeNumber; + } + + @Override + public void run() { + try { + List toDelete = new ArrayList<>(); + for (RuleBasedSegment segment : mToRemove) { + toDelete.add(mCipher.encrypt(segment.getName())); + } + + List toAdd = new ArrayList<>(); + for (RuleBasedSegment segment : mToAdd) { + String name = mCipher.encrypt(segment.getName()); + String body = mCipher.encrypt(Json.toJson(segment)); + toAdd.add(new RuleBasedSegmentEntity(name, body, System.currentTimeMillis())); + } + + mDao.delete(toDelete); + mDao.insert(toAdd); + mGeneralInfoStorage.setRbsChangeNumber(mChangeNumber); + } catch (Exception e) { + Logger.e("Error updating RBS: " + e.getLocalizedMessage()); + throw e; + } + } +} \ No newline at end of file diff --git a/src/test/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageTest.java b/src/test/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageTest.java new file mode 100644 index 000000000..540548ff2 --- /dev/null +++ b/src/test/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageTest.java @@ -0,0 +1,6 @@ +package io.split.android.client.storage.rbs; + +public class SqLitePersistentRuleBasedSegmentStorageTest { + + +} From 2b2b4249f21dec3e5acbd688017abd5422203f22 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Tue, 25 Feb 2025 16:49:51 -0300 Subject: [PATCH 03/26] SqLite persistent storage tests --- src/sharedTest/java/helper/TestingHelper.java | 15 ++- ...PersistentRuleBasedSegmentStorageTest.java | 105 +++++++++++++++++- 2 files changed, 116 insertions(+), 4 deletions(-) diff --git a/src/sharedTest/java/helper/TestingHelper.java b/src/sharedTest/java/helper/TestingHelper.java index 2d70138d2..af190706d 100644 --- a/src/sharedTest/java/helper/TestingHelper.java +++ b/src/sharedTest/java/helper/TestingHelper.java @@ -1,5 +1,8 @@ package helper; +import static java.lang.Thread.sleep; + +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.concurrent.BlockingQueue; @@ -12,8 +15,6 @@ import io.split.android.client.events.SplitEventTask; import io.split.android.client.utils.logger.Logger; -import static java.lang.Thread.sleep; - public class TestingHelper { public static final String COUNTERS_REFRESH_RATE_SECS_NAME = "COUNTERS_REFRESH_RATE_SECS"; @@ -120,4 +121,14 @@ public static KeyImpression newImpression(String feature, String key) { impression.time = 100; return impression; } + + public static Object getFieldValue(Object object, String fieldName) { + try { + Field field = object.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + return field.get(object); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException("Error accessing field: " + fieldName, e); + } + } } diff --git a/src/test/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageTest.java b/src/test/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageTest.java index 540548ff2..fc29bc70e 100644 --- a/src/test/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageTest.java +++ b/src/test/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageTest.java @@ -1,6 +1,107 @@ package io.split.android.client.storage.rbs; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static helper.TestingHelper.getFieldValue; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.util.HashSet; +import java.util.Set; + +import io.split.android.client.dtos.RuleBasedSegment; +import io.split.android.client.storage.cipher.SplitCipher; +import io.split.android.client.storage.db.SplitRoomDatabase; +import io.split.android.client.storage.db.rbs.RuleBasedSegmentDao; +import io.split.android.client.storage.general.GeneralInfoStorage; + public class SqLitePersistentRuleBasedSegmentStorageTest { - -} + private SplitCipher mCipher; + private SplitRoomDatabase mDatabase; + private RuleBasedSegmentDao mDao; + private GeneralInfoStorage mGeneralInfoStorage; + + private SqLitePersistentRuleBasedSegmentStorage storage; + + @Before + public void setUp() { + mCipher = mock(SplitCipher.class); + mDatabase = mock(SplitRoomDatabase.class); + mDao = mock(RuleBasedSegmentDao.class); + mGeneralInfoStorage = mock(GeneralInfoStorage.class); + when(mDatabase.ruleBasedSegmentDao()).thenReturn(mDao); + + storage = new SqLitePersistentRuleBasedSegmentStorage(mCipher, mDatabase, mGeneralInfoStorage); + } + + @Test + public void getSnapshotBuildsAndRunsSnapshotLoaderInstanceInTransaction() { + RuleBasedSegmentSnapshot expectedSnapshot = mock(RuleBasedSegmentSnapshot.class); + when(mDatabase.runInTransaction((SnapshotLoader) any())).thenReturn(expectedSnapshot); + + RuleBasedSegmentSnapshot result = storage.getSnapshot(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(SnapshotLoader.class); + verify(mDatabase).runInTransaction(captor.capture()); + SnapshotLoader snapshotLoader = captor.getValue(); + assertSame(mDao, getFieldValue(snapshotLoader, "mDao")); + assertSame(mCipher, getFieldValue(snapshotLoader, "mCipher")); + assertSame(mGeneralInfoStorage, getFieldValue(snapshotLoader, "mGeneralInfoStorage")); + assertSame(expectedSnapshot, result); + } + + @Test + public void updateBuildsAndRunsUpdaterInstanceInTransaction() { + Set toAdd = new HashSet<>(); + Set toRemove = new HashSet<>(); + long changeNumber = 123L; + + storage.update(toAdd, toRemove, changeNumber); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Updater.class); + verify(mDatabase).runInTransaction(captor.capture()); + Updater updater = captor.getValue(); + assertSame(mCipher, getFieldValue(updater, "mCipher")); + assertSame(mDao, getFieldValue(updater, "mDao")); + assertSame(mGeneralInfoStorage, getFieldValue(updater, "mGeneralInfoStorage")); + assertSame(toAdd, getFieldValue(updater, "mToAdd")); + assertSame(toRemove, getFieldValue(updater, "mToRemove")); + assertSame(changeNumber, getFieldValue(updater, "mChangeNumber")); + } + + @Test + public void clearBuildsAndRunsClearerInstanceInTransaction() { + storage.clear(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Clearer.class); + verify(mDatabase).runInTransaction(captor.capture()); + Clearer clearer = captor.getValue(); + assertSame(mDao, getFieldValue(clearer, "mDao")); + assertSame(mGeneralInfoStorage, getFieldValue(clearer, "mGeneralInfoStorage")); + } + + @Test + public void cipherCannotBeNull() { + assertThrows(NullPointerException.class, + () -> new SqLitePersistentRuleBasedSegmentStorage(null, mDatabase, mGeneralInfoStorage)); + } + + @Test + public void databaseCannotBeNull() { + assertThrows(NullPointerException.class, + () -> new SqLitePersistentRuleBasedSegmentStorage(mCipher, null, mGeneralInfoStorage)); + } + + @Test + public void generalInfoStorageCannotBeNull() { + assertThrows(NullPointerException.class, + () -> new SqLitePersistentRuleBasedSegmentStorage(mCipher, mDatabase, null)); + } +} \ No newline at end of file From e292005276b899a285e41aff299d20a186a0f248 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Tue, 25 Feb 2025 17:29:05 -0300 Subject: [PATCH 04/26] Tests --- .../client/storage/rbs/SnapshotLoader.java | 2 +- .../rbs/RuleBasedSegmentStorageImplTest.java | 2 +- .../storage/rbs/SnapshotLoaderTest.java | 118 ++++++++++++++++++ .../client/storage/rbs/UpdaterTest.java | 101 +++++++++++++++ 4 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 src/test/java/io/split/android/client/storage/rbs/SnapshotLoaderTest.java create mode 100644 src/test/java/io/split/android/client/storage/rbs/UpdaterTest.java diff --git a/src/main/java/io/split/android/client/storage/rbs/SnapshotLoader.java b/src/main/java/io/split/android/client/storage/rbs/SnapshotLoader.java index 11cea6182..716ba00fd 100644 --- a/src/main/java/io/split/android/client/storage/rbs/SnapshotLoader.java +++ b/src/main/java/io/split/android/client/storage/rbs/SnapshotLoader.java @@ -46,7 +46,7 @@ private Map convertToDTOs(List if (entities != null) { for (RuleBasedSegmentEntity entity : entities) { String name = mCipher.decrypt(entity.getName()); - String body = mCipher.encrypt(entity.getBody()); + String body = mCipher.decrypt(entity.getBody()); if (name == null || body == null) { continue; } diff --git a/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java b/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java index 4596bf921..d19b02225 100644 --- a/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java +++ b/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java @@ -193,7 +193,7 @@ public void clearCallsClearOnPersistentStorage() { verify(mPersistentStorage).clear(); } - private static RuleBasedSegment createRuleBasedSegment(String name) { + static RuleBasedSegment createRuleBasedSegment(String name) { return new RuleBasedSegment(name, "user", 1, diff --git a/src/test/java/io/split/android/client/storage/rbs/SnapshotLoaderTest.java b/src/test/java/io/split/android/client/storage/rbs/SnapshotLoaderTest.java new file mode 100644 index 000000000..da61849c6 --- /dev/null +++ b/src/test/java/io/split/android/client/storage/rbs/SnapshotLoaderTest.java @@ -0,0 +1,118 @@ +package io.split.android.client.storage.rbs; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import io.split.android.client.dtos.RuleBasedSegment; +import io.split.android.client.storage.cipher.SplitCipher; +import io.split.android.client.storage.db.rbs.RuleBasedSegmentDao; +import io.split.android.client.storage.db.rbs.RuleBasedSegmentEntity; +import io.split.android.client.storage.general.GeneralInfoStorage; + +public class SnapshotLoaderTest { + + private RuleBasedSegmentDao mDao; + private SplitCipher mCipher; + private GeneralInfoStorage mGeneralInfoStorage; + private SnapshotLoader mSnapshotLoader; + + @Before + public void setUp() { + mDao = mock(RuleBasedSegmentDao.class); + mCipher = mock(SplitCipher.class); + mGeneralInfoStorage = mock(GeneralInfoStorage.class); + mSnapshotLoader = new SnapshotLoader(mDao, mCipher, mGeneralInfoStorage); + } + + @Test + public void callReturnsCorrectSnapshotWithDecryptedSegments() throws Exception { + long expectedChangeNumber = 123L; + when(mGeneralInfoStorage.getFlagsChangeNumber()).thenReturn(expectedChangeNumber); + + RuleBasedSegmentEntity entity1 = new RuleBasedSegmentEntity("segment1", "encryptedBody1", System.currentTimeMillis()); + RuleBasedSegmentEntity entity2 = new RuleBasedSegmentEntity("segment2", "encryptedBody2", System.currentTimeMillis()); + List entities = Arrays.asList(entity1, entity2); + when(mDao.getAll()).thenReturn(entities); + + when(mCipher.decrypt("segment1")).thenReturn("segment1"); + when(mCipher.decrypt("segment2")).thenReturn("segment2"); + when(mCipher.decrypt("encryptedBody1")).thenAnswer(invocation -> "{ \"name\": \"segment1\", \"trafficTypeName\": \"user\", \"changeNumber\": 1 }"); + when(mCipher.decrypt("encryptedBody2")).thenAnswer(invocation -> "{ \"name\": \"segment2\", \"trafficTypeName\": \"user\", \"changeNumber\": 2 }"); + + RuleBasedSegmentSnapshot result = mSnapshotLoader.call(); + + assertNotNull(result); + assertEquals(expectedChangeNumber, result.getChangeNumber()); + + Map segments = result.getSegments(); + assertEquals(2, segments.size()); + RuleBasedSegment rbs1 = segments.get("segment1"); + assertNotNull(rbs1); + assertEquals("segment1", rbs1.getName()); + assertEquals("user", rbs1.getTrafficTypeName()); + assertEquals(1, rbs1.getChangeNumber()); + + RuleBasedSegment rbs2 = segments.get("segment2"); + assertNotNull(rbs2); + assertEquals("segment2", rbs2.getName()); + assertEquals("user", rbs2.getTrafficTypeName()); + assertEquals(2, rbs2.getChangeNumber()); + } + + @Test + public void callGetsChangeNumberFromGeneralInfoStorage() { + mSnapshotLoader.call(); + + verify(mGeneralInfoStorage).getFlagsChangeNumber(); + } + + @Test + public void callGetsAllSegmentsFromDao() { + mSnapshotLoader.call(); + + verify(mDao).getAll(); + } + + @Test + public void callDecryptsNameAndBodyFromEntity() { + when(mDao.getAll()).thenReturn(Arrays.asList( + new RuleBasedSegmentEntity("segment1", "encryptedBody1", System.currentTimeMillis()), + new RuleBasedSegmentEntity("segment2", "encryptedBody2", System.currentTimeMillis()))); + + mSnapshotLoader.call(); + + verify(mCipher).decrypt("segment1"); + verify(mCipher).decrypt("segment2"); + verify(mCipher).decrypt("encryptedBody1"); + verify(mCipher).decrypt("encryptedBody2"); + } + + @Test + public void constructorThrowsNullPointerExceptionWhenDaoIsNull() { + assertThrows(NullPointerException.class, + () -> new SnapshotLoader(null, mCipher, mGeneralInfoStorage)); + } + + @Test + public void constructorThrowsNullPointerExceptionWhenCipherIsNull() { + assertThrows(NullPointerException.class, + () -> new SnapshotLoader(mDao, null, mGeneralInfoStorage)); + } + + @Test + public void constructorThrowsNullPointerExceptionWhenGeneralInfoStorageIsNull() { + assertThrows(NullPointerException.class, + () -> new SnapshotLoader(mDao, mCipher, null)); + } +} \ No newline at end of file diff --git a/src/test/java/io/split/android/client/storage/rbs/UpdaterTest.java b/src/test/java/io/split/android/client/storage/rbs/UpdaterTest.java new file mode 100644 index 000000000..d7820b313 --- /dev/null +++ b/src/test/java/io/split/android/client/storage/rbs/UpdaterTest.java @@ -0,0 +1,101 @@ +package io.split.android.client.storage.rbs; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static io.split.android.client.storage.rbs.RuleBasedSegmentStorageImplTest.createRuleBasedSegment; + +import androidx.annotation.NonNull; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentMatcher; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Set; + +import io.split.android.client.dtos.RuleBasedSegment; +import io.split.android.client.storage.cipher.SplitCipher; +import io.split.android.client.storage.db.rbs.RuleBasedSegmentDao; +import io.split.android.client.storage.db.rbs.RuleBasedSegmentEntity; +import io.split.android.client.storage.general.GeneralInfoStorage; + +public class UpdaterTest { + + private SplitCipher mCipher; + private RuleBasedSegmentDao mDao; + private GeneralInfoStorage mGeneralInfoStorage; + private Updater mUpdater; + + @Before + public void setUp() { + mCipher = mock(SplitCipher.class); + mDao = mock(RuleBasedSegmentDao.class); + mGeneralInfoStorage = mock(GeneralInfoStorage.class); + } + + @Test + public void runEncryptsRemovedSegmentNamesBeforeSendingToDao() { + Set toRemove = Set.of( + createRuleBasedSegment("segment1"), createRuleBasedSegment("segment2")); + when(mCipher.encrypt(any())).thenAnswer(invocation -> "encrypted_" + invocation.getArgument(0)); + mUpdater = createUpdater(Collections.emptySet(), toRemove, 10); + + mUpdater.run(); + + verify(mCipher).encrypt("segment1"); + verify(mCipher).encrypt("segment2"); + verify(mDao).delete(argThat(new ArgumentMatcher>() { + @Override + public boolean matches(List argument) { + return argument.size() == 2 && + argument.contains("encrypted_segment1") && + argument.contains("encrypted_segment2"); + } + })); + } + + @Test + public void runEncryptsAddedSegmentNamesBeforeSendingToDao() { + Set toAdd = Set.of( + createRuleBasedSegment("segment1"), createRuleBasedSegment("segment2")); + when(mCipher.encrypt(any())).thenAnswer(invocation -> "encrypted_" + invocation.getArgument(0)); + mUpdater = createUpdater(toAdd, Collections.emptySet(), 10); + + mUpdater.run(); + + verify(mCipher).encrypt("segment1"); + verify(mCipher).encrypt("segment2"); + verify(mDao).insert(argThat(new ArgumentMatcher>() { + @Override + public boolean matches(List argument) { + argument.sort(Comparator.comparing(RuleBasedSegmentEntity::getName)); + RuleBasedSegmentEntity ruleBasedSegmentEntity = argument.get(0); + RuleBasedSegmentEntity ruleBasedSegmentEntity1 = argument.get(1); + return argument.size() == 2 && + ruleBasedSegmentEntity.getName().equals("encrypted_segment1") && + ruleBasedSegmentEntity1.getName().equals("encrypted_segment2") && + ruleBasedSegmentEntity.getBody().startsWith("encrypted_") && + ruleBasedSegmentEntity1.getBody().startsWith("encrypted_"); + } + })); + } + + @Test + public void runUpdatesChangeNumber() { + mUpdater = createUpdater(Collections.emptySet(), Collections.emptySet(), 10); + + mUpdater.run(); + + verify(mGeneralInfoStorage).setRbsChangeNumber(10); + } + + @NonNull + private Updater createUpdater(Set toAdd, Set toRemove, long changeNumber) { + return new Updater(mCipher, mDao, mGeneralInfoStorage, toAdd, toRemove, changeNumber); + } +} From bae1f83294121755c4c81a6c5ccfe38ce7288405 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Tue, 25 Feb 2025 17:37:26 -0300 Subject: [PATCH 05/26] Fixes --- .../android/client/storage/rbs/Clearer.java | 6 +++-- .../android/client/storage/rbs/Updater.java | 2 +- .../storage/rbs/SnapshotLoaderTest.java | 2 +- ...PersistentRuleBasedSegmentStorageTest.java | 2 +- ...SegmentsPersistentStorageProviderTest.java | 22 +++++++++++++++++++ 5 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 src/test/java/io/split/android/client/storage/rbs/SqLiteRuleBasedSegmentsPersistentStorageProviderTest.java diff --git a/src/main/java/io/split/android/client/storage/rbs/Clearer.java b/src/main/java/io/split/android/client/storage/rbs/Clearer.java index 30327f124..1e0ab4a9d 100644 --- a/src/main/java/io/split/android/client/storage/rbs/Clearer.java +++ b/src/main/java/io/split/android/client/storage/rbs/Clearer.java @@ -1,5 +1,7 @@ package io.split.android.client.storage.rbs; +import static io.split.android.client.utils.Utils.checkNotNull; + import io.split.android.client.storage.db.rbs.RuleBasedSegmentDao; import io.split.android.client.storage.general.GeneralInfoStorage; import io.split.android.client.utils.logger.Logger; @@ -10,8 +12,8 @@ class Clearer implements Runnable { private final GeneralInfoStorage mGeneralInfoStorage; public Clearer(RuleBasedSegmentDao dao, GeneralInfoStorage generalInfoStorage) { - mDao = dao; - mGeneralInfoStorage = generalInfoStorage; + mDao = checkNotNull(dao); + mGeneralInfoStorage = checkNotNull(generalInfoStorage); } @Override diff --git a/src/main/java/io/split/android/client/storage/rbs/Updater.java b/src/main/java/io/split/android/client/storage/rbs/Updater.java index 375d03968..93fb76ac4 100644 --- a/src/main/java/io/split/android/client/storage/rbs/Updater.java +++ b/src/main/java/io/split/android/client/storage/rbs/Updater.java @@ -67,4 +67,4 @@ public void run() { throw e; } } -} \ No newline at end of file +} diff --git a/src/test/java/io/split/android/client/storage/rbs/SnapshotLoaderTest.java b/src/test/java/io/split/android/client/storage/rbs/SnapshotLoaderTest.java index da61849c6..50bc36d81 100644 --- a/src/test/java/io/split/android/client/storage/rbs/SnapshotLoaderTest.java +++ b/src/test/java/io/split/android/client/storage/rbs/SnapshotLoaderTest.java @@ -115,4 +115,4 @@ public void constructorThrowsNullPointerExceptionWhenGeneralInfoStorageIsNull() assertThrows(NullPointerException.class, () -> new SnapshotLoader(mDao, mCipher, null)); } -} \ No newline at end of file +} diff --git a/src/test/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageTest.java b/src/test/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageTest.java index fc29bc70e..13c646e0c 100644 --- a/src/test/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageTest.java +++ b/src/test/java/io/split/android/client/storage/rbs/SqLitePersistentRuleBasedSegmentStorageTest.java @@ -104,4 +104,4 @@ public void generalInfoStorageCannotBeNull() { assertThrows(NullPointerException.class, () -> new SqLitePersistentRuleBasedSegmentStorage(mCipher, mDatabase, null)); } -} \ No newline at end of file +} diff --git a/src/test/java/io/split/android/client/storage/rbs/SqLiteRuleBasedSegmentsPersistentStorageProviderTest.java b/src/test/java/io/split/android/client/storage/rbs/SqLiteRuleBasedSegmentsPersistentStorageProviderTest.java new file mode 100644 index 000000000..a6cd2dee8 --- /dev/null +++ b/src/test/java/io/split/android/client/storage/rbs/SqLiteRuleBasedSegmentsPersistentStorageProviderTest.java @@ -0,0 +1,22 @@ +package io.split.android.client.storage.rbs; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import org.junit.Test; + +import io.split.android.client.storage.cipher.SplitCipher; +import io.split.android.client.storage.db.SplitRoomDatabase; +import io.split.android.client.storage.general.GeneralInfoStorage; + +public class SqLiteRuleBasedSegmentsPersistentStorageProviderTest { + + @Test + public void providesSqLiteImplementation() { + PersistentRuleBasedSegmentStorage.Provider provider = + new SqLitePersistentRuleBasedSegmentStorageProvider(mock(SplitCipher.class), mock(SplitRoomDatabase.class), mock(GeneralInfoStorage.class)); + PersistentRuleBasedSegmentStorage persistentRuleBasedSegmentStorage = provider.get(); + + assertTrue(persistentRuleBasedSegmentStorage instanceof SqLitePersistentRuleBasedSegmentStorage); + } +} From c1c96dd2ae5c91f3c000803b29bfb5811123d5a3 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Tue, 25 Feb 2025 17:42:42 -0300 Subject: [PATCH 06/26] Add exception handling --- .../split/android/client/storage/rbs/SnapshotLoader.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/split/android/client/storage/rbs/SnapshotLoader.java b/src/main/java/io/split/android/client/storage/rbs/SnapshotLoader.java index 716ba00fd..16ddb1075 100644 --- a/src/main/java/io/split/android/client/storage/rbs/SnapshotLoader.java +++ b/src/main/java/io/split/android/client/storage/rbs/SnapshotLoader.java @@ -51,8 +51,12 @@ private Map convertToDTOs(List continue; } - RuleBasedSegment ruleBasedSegment = Json.fromJson(body, RuleBasedSegment.class); - segments.put(name, ruleBasedSegment); + try { + RuleBasedSegment ruleBasedSegment = Json.fromJson(body, RuleBasedSegment.class); + segments.put(name, ruleBasedSegment); + } catch (Exception e) { + Logger.e("Error parsing RBS with name " + name + ": " + e.getLocalizedMessage()); + } } } return segments; From ad2332297075ecc376479a45cf5d5d2e70ec7318 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Tue, 25 Feb 2025 17:54:02 -0300 Subject: [PATCH 07/26] More tests --- .../android/client/storage/rbs/Updater.java | 22 +++++++++++++---- .../client/storage/rbs/UpdaterTest.java | 24 +++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/split/android/client/storage/rbs/Updater.java b/src/main/java/io/split/android/client/storage/rbs/Updater.java index 93fb76ac4..12e5c3fb2 100644 --- a/src/main/java/io/split/android/client/storage/rbs/Updater.java +++ b/src/main/java/io/split/android/client/storage/rbs/Updater.java @@ -49,14 +49,28 @@ public void run() { try { List toDelete = new ArrayList<>(); for (RuleBasedSegment segment : mToRemove) { - toDelete.add(mCipher.encrypt(segment.getName())); + String encryptedName = mCipher.encrypt(segment.getName()); + if (encryptedName != null) { + toDelete.add(encryptedName); + } } List toAdd = new ArrayList<>(); for (RuleBasedSegment segment : mToAdd) { - String name = mCipher.encrypt(segment.getName()); - String body = mCipher.encrypt(Json.toJson(segment)); - toAdd.add(new RuleBasedSegmentEntity(name, body, System.currentTimeMillis())); + if (segment == null) { + continue; + } + + try { + String name = mCipher.encrypt(segment.getName()); + String body = mCipher.encrypt(Json.toJson(segment)); + if (name == null || body == null) { + continue; + } + toAdd.add(new RuleBasedSegmentEntity(name, body, System.currentTimeMillis())); + } catch (Exception e) { + Logger.e("Error parsing RBS with name " + segment.getName() + ": " + e.getLocalizedMessage()); + } } mDao.delete(toDelete); diff --git a/src/test/java/io/split/android/client/storage/rbs/UpdaterTest.java b/src/test/java/io/split/android/client/storage/rbs/UpdaterTest.java index d7820b313..edb779f98 100644 --- a/src/test/java/io/split/android/client/storage/rbs/UpdaterTest.java +++ b/src/test/java/io/split/android/client/storage/rbs/UpdaterTest.java @@ -1,6 +1,7 @@ package io.split.android.client.storage.rbs; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -94,6 +95,29 @@ public void runUpdatesChangeNumber() { verify(mGeneralInfoStorage).setRbsChangeNumber(10); } + @Test + public void runDoesNotUpdateSegmentIfEncryptedNameIsNull() { + Set toAdd = Set.of( + createRuleBasedSegment("segment1"), createRuleBasedSegment("segment2")); + Set toRemove = Set.of( + createRuleBasedSegment("segment3"), createRuleBasedSegment("segment4")); + when(mCipher.encrypt(anyString())).thenReturn(null); + when(mCipher.encrypt(argThat(argument -> argument.contains("segment1")))).thenReturn("encrypted_segment1"); + when(mCipher.encrypt("segment3")).thenReturn("encrypted_segment3"); + mUpdater = createUpdater(toAdd, toRemove, 10); + + mUpdater.run(); + + verify(mCipher).encrypt("segment1"); + verify(mCipher).encrypt("segment2"); + verify(mCipher).encrypt("segment3"); + verify(mCipher).encrypt("segment4"); + verify(mDao).delete(argThat(argument -> argument.size() == 1 && + argument.get(0).equals("encrypted_segment3"))); + verify(mDao).insert(argThat((ArgumentMatcher>) argument -> argument.size() == 1 && + argument.get(0).getName().equals("encrypted_segment1"))); + } + @NonNull private Updater createUpdater(Set toAdd, Set toRemove, long changeNumber) { return new Updater(mCipher, mDao, mGeneralInfoStorage, toAdd, toRemove, changeNumber); From 2fc3f2ed8c09435396012774ab74cb3a14e75860 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Tue, 25 Feb 2025 18:08:20 -0300 Subject: [PATCH 08/26] Add nullability annotations --- .../client/storage/rbs/RuleBasedSegmentSnapshot.java | 4 +++- .../io/split/android/client/storage/rbs/SnapshotLoader.java | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentSnapshot.java b/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentSnapshot.java index d62e31a42..69f050398 100644 --- a/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentSnapshot.java +++ b/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentSnapshot.java @@ -2,6 +2,8 @@ import static io.split.android.client.utils.Utils.checkNotNull; +import androidx.annotation.NonNull; + import java.util.Map; import io.split.android.client.dtos.RuleBasedSegment; @@ -12,7 +14,7 @@ public class RuleBasedSegmentSnapshot { private final long mChangeNumber; - public RuleBasedSegmentSnapshot(Map segments, long changeNumber) { + public RuleBasedSegmentSnapshot(@NonNull Map segments, long changeNumber) { mSegments = checkNotNull(segments); mChangeNumber = changeNumber; } diff --git a/src/main/java/io/split/android/client/storage/rbs/SnapshotLoader.java b/src/main/java/io/split/android/client/storage/rbs/SnapshotLoader.java index 16ddb1075..d56a54a66 100644 --- a/src/main/java/io/split/android/client/storage/rbs/SnapshotLoader.java +++ b/src/main/java/io/split/android/client/storage/rbs/SnapshotLoader.java @@ -2,6 +2,9 @@ import static io.split.android.client.utils.Utils.checkNotNull; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import java.util.HashMap; import java.util.List; import java.util.Map; @@ -41,7 +44,8 @@ public RuleBasedSegmentSnapshot call() { } } - private Map convertToDTOs(List entities) { + @NonNull + private Map convertToDTOs(@Nullable List entities) { Map segments = new HashMap<>(); if (entities != null) { for (RuleBasedSegmentEntity entity : entities) { From a8bff33f4bf7cb667ccdcfdd60c39e282dbe8128 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Thu, 27 Feb 2025 12:50:06 -0300 Subject: [PATCH 09/26] Prep for ParserCommons --- .../android/client/dtos/MatcherType.java | 5 ++- .../LocalhostRuleBasedSegmentsStorage.java | 44 +++++++++++++++++++ ...lhostRuleBasedSegmentsStorageProvider.java | 21 +++++++++ .../client/storage/db/StorageFactory.java | 12 +++++ .../LazyRuleBasedSegmentStorageProvider.java | 28 ++++++++++++ .../rbs/RuleBasedSegmentStorageProvider.java | 9 ++++ 6 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorage.java create mode 100644 src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorageProvider.java create mode 100644 src/main/java/io/split/android/client/storage/rbs/LazyRuleBasedSegmentStorageProvider.java create mode 100644 src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageProvider.java diff --git a/src/main/java/io/split/android/client/dtos/MatcherType.java b/src/main/java/io/split/android/client/dtos/MatcherType.java index 580e97f2e..75d5325b7 100644 --- a/src/main/java/io/split/android/client/dtos/MatcherType.java +++ b/src/main/java/io/split/android/client/dtos/MatcherType.java @@ -60,5 +60,8 @@ public enum MatcherType { @SerializedName("BETWEEN_SEMVER") BETWEEN_SEMVER, @SerializedName("IN_LIST_SEMVER") - IN_LIST_SEMVER + IN_LIST_SEMVER, + + @SerializedName("IN_RULE_BASED_SEGMENT") + IN_RULE_BASED_SEGMENT, } diff --git a/src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorage.java b/src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorage.java new file mode 100644 index 000000000..137b1f6b0 --- /dev/null +++ b/src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorage.java @@ -0,0 +1,44 @@ +package io.split.android.client.localhost; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.Set; + +import io.split.android.client.dtos.RuleBasedSegment; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; +import io.split.android.engine.experiments.ParsedRuleBasedSegment; + +public class LocalhostRuleBasedSegmentsStorage implements RuleBasedSegmentStorage { + + @Nullable + @Override + public ParsedRuleBasedSegment get(String segmentName, String matchingKey) { + return null; + } + + @Override + public boolean update(@NonNull Set toAdd, @NonNull Set toRemove, long changeNumber) { + return false; + } + + @Override + public long getChangeNumber() { + return -1; + } + + @Override + public boolean contains(@NonNull Set segmentNames) { + return false; + } + + @Override + public void loadLocal() { + // no-op + } + + @Override + public void clear() { + // no-op + } +} diff --git a/src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorageProvider.java b/src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorageProvider.java new file mode 100644 index 000000000..2b5b10e10 --- /dev/null +++ b/src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorageProvider.java @@ -0,0 +1,21 @@ +package io.split.android.client.localhost; + +import androidx.annotation.NonNull; + +import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; + +class LocalhostRuleBasedSegmentsStorageProvider implements RuleBasedSegmentStorageProvider { + + private final RuleBasedSegmentStorage mRuleBasedSegmentStorage; + + LocalhostRuleBasedSegmentsStorageProvider(@NonNull RuleBasedSegmentStorage ruleBasedSegmentStorage) { + mRuleBasedSegmentStorage = ruleBasedSegmentStorage; + } + + @NonNull + @Override + public RuleBasedSegmentStorage get() { + return mRuleBasedSegmentStorage; + } +} diff --git a/src/main/java/io/split/android/client/storage/db/StorageFactory.java b/src/main/java/io/split/android/client/storage/db/StorageFactory.java index 2960f73f7..45b7b18bf 100644 --- a/src/main/java/io/split/android/client/storage/db/StorageFactory.java +++ b/src/main/java/io/split/android/client/storage/db/StorageFactory.java @@ -30,6 +30,10 @@ import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; import io.split.android.client.storage.mysegments.MySegmentsStorageContainerImpl; import io.split.android.client.storage.mysegments.SqLitePersistentMySegmentsStorage; +import io.split.android.client.storage.rbs.LazyRuleBasedSegmentStorageProvider; +import io.split.android.client.storage.rbs.PersistentRuleBasedSegmentStorage; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; +import io.split.android.client.storage.rbs.SqLitePersistentRuleBasedSegmentStorageProvider; import io.split.android.client.storage.splits.PersistentSplitsStorage; import io.split.android.client.storage.splits.SplitsStorage; import io.split.android.client.storage.splits.SplitsStorageImpl; @@ -154,4 +158,12 @@ public static PersistentImpressionsObserverCacheStorage getImpressionsObserverCa public static GeneralInfoStorage getGeneralInfoStorage(SplitRoomDatabase splitRoomDatabase) { return new GeneralInfoStorageImpl(splitRoomDatabase.generalInfoDao()); } + + public static PersistentRuleBasedSegmentStorage getPersistentRuleBasedSegmentStorage(SplitRoomDatabase splitRoomDatabase, SplitCipher splitCipher, GeneralInfoStorage generalInfoStorage) { + return new SqLitePersistentRuleBasedSegmentStorageProvider(splitCipher, splitRoomDatabase, generalInfoStorage).get(); + } + + public static RuleBasedSegmentStorageProvider getRuleBasedSegmentStorageProvider() { + return new LazyRuleBasedSegmentStorageProvider(); + } } diff --git a/src/main/java/io/split/android/client/storage/rbs/LazyRuleBasedSegmentStorageProvider.java b/src/main/java/io/split/android/client/storage/rbs/LazyRuleBasedSegmentStorageProvider.java new file mode 100644 index 000000000..26977735d --- /dev/null +++ b/src/main/java/io/split/android/client/storage/rbs/LazyRuleBasedSegmentStorageProvider.java @@ -0,0 +1,28 @@ +package io.split.android.client.storage.rbs; + +import androidx.annotation.NonNull; + +import java.util.concurrent.atomic.AtomicReference; + +import io.split.android.client.utils.logger.Logger; + +public class LazyRuleBasedSegmentStorageProvider implements RuleBasedSegmentStorageProvider { + + private final AtomicReference mRuleBasedSegmentStorageRef = new AtomicReference<>(); + + public void set(@NonNull RuleBasedSegmentStorage ruleBasedSegmentStorage) { + if (!mRuleBasedSegmentStorageRef.compareAndSet(null, ruleBasedSegmentStorage)) { + Logger.w("RuleBasedSegmentStorage already set in LazyRuleBasedSegmentStorageProvider"); + } + } + + @NonNull + @Override + public RuleBasedSegmentStorage get() { + RuleBasedSegmentStorage storage = mRuleBasedSegmentStorageRef.get(); + if (storage == null) { + throw new IllegalStateException("RuleBasedSegmentStorage not set in LazyRuleBasedSegmentStorageProvider"); + } + return storage; + } +} diff --git a/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageProvider.java b/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageProvider.java new file mode 100644 index 000000000..2ccb00c69 --- /dev/null +++ b/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageProvider.java @@ -0,0 +1,9 @@ +package io.split.android.client.storage.rbs; + +import androidx.annotation.NonNull; + +public interface RuleBasedSegmentStorageProvider { + + @NonNull + RuleBasedSegmentStorage get(); +} From 537ed77beef717b33182511c089ea1cc34add395 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Thu, 27 Feb 2025 12:57:39 -0300 Subject: [PATCH 10/26] Tests for lazy storage provider --- ...lhostRuleBasedSegmentsStorageProvider.java | 3 +- .../LazyRuleBasedSegmentStorageProvider.java | 9 ++---- .../rbs/RuleBasedSegmentStorageProvider.java | 4 +-- ...zyRuleBasedSegmentStorageProviderTest.java | 29 +++++++++++++++++++ 4 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 src/test/java/io/split/android/client/storage/rbs/LazyRuleBasedSegmentStorageProviderTest.java diff --git a/src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorageProvider.java b/src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorageProvider.java index 2b5b10e10..9c4ae9e77 100644 --- a/src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorageProvider.java +++ b/src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorageProvider.java @@ -12,8 +12,7 @@ class LocalhostRuleBasedSegmentsStorageProvider implements RuleBasedSegmentStora LocalhostRuleBasedSegmentsStorageProvider(@NonNull RuleBasedSegmentStorage ruleBasedSegmentStorage) { mRuleBasedSegmentStorage = ruleBasedSegmentStorage; } - - @NonNull + @Override public RuleBasedSegmentStorage get() { return mRuleBasedSegmentStorage; diff --git a/src/main/java/io/split/android/client/storage/rbs/LazyRuleBasedSegmentStorageProvider.java b/src/main/java/io/split/android/client/storage/rbs/LazyRuleBasedSegmentStorageProvider.java index 26977735d..9a68ebb2b 100644 --- a/src/main/java/io/split/android/client/storage/rbs/LazyRuleBasedSegmentStorageProvider.java +++ b/src/main/java/io/split/android/client/storage/rbs/LazyRuleBasedSegmentStorageProvider.java @@ -1,6 +1,7 @@ package io.split.android.client.storage.rbs; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.util.concurrent.atomic.AtomicReference; @@ -16,13 +17,9 @@ public void set(@NonNull RuleBasedSegmentStorage ruleBasedSegmentStorage) { } } - @NonNull + @Nullable @Override public RuleBasedSegmentStorage get() { - RuleBasedSegmentStorage storage = mRuleBasedSegmentStorageRef.get(); - if (storage == null) { - throw new IllegalStateException("RuleBasedSegmentStorage not set in LazyRuleBasedSegmentStorageProvider"); - } - return storage; + return mRuleBasedSegmentStorageRef.get(); } } diff --git a/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageProvider.java b/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageProvider.java index 2ccb00c69..4c4393a24 100644 --- a/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageProvider.java +++ b/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageProvider.java @@ -1,9 +1,9 @@ package io.split.android.client.storage.rbs; -import androidx.annotation.NonNull; +import androidx.annotation.Nullable; public interface RuleBasedSegmentStorageProvider { - @NonNull + @Nullable RuleBasedSegmentStorage get(); } diff --git a/src/test/java/io/split/android/client/storage/rbs/LazyRuleBasedSegmentStorageProviderTest.java b/src/test/java/io/split/android/client/storage/rbs/LazyRuleBasedSegmentStorageProviderTest.java new file mode 100644 index 000000000..01b84b6a7 --- /dev/null +++ b/src/test/java/io/split/android/client/storage/rbs/LazyRuleBasedSegmentStorageProviderTest.java @@ -0,0 +1,29 @@ +package io.split.android.client.storage.rbs; + +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.mockito.Mockito.mock; + +import org.junit.Test; + +public class LazyRuleBasedSegmentStorageProviderTest { + + @Test + public void refCanOnlyBeSetOnce() { + LazyRuleBasedSegmentStorageProvider provider = new LazyRuleBasedSegmentStorageProvider(); + RuleBasedSegmentStorage firstInstance = mock(RuleBasedSegmentStorage.class); + RuleBasedSegmentStorage secondInstance = mock(RuleBasedSegmentStorage.class); + provider.set(firstInstance); + provider.set(secondInstance); + + assertSame(firstInstance, provider.get()); + assertNotSame(secondInstance, provider.get()); + } + + @Test + public void getReturnsNullWhenSetHasNotBeenCalled() { + LazyRuleBasedSegmentStorageProvider provider = new LazyRuleBasedSegmentStorageProvider(); + assertNull(provider.get()); + } +} From 7519aca029781caa0104ce42a559f442a732d846 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Thu, 27 Feb 2025 14:09:39 -0300 Subject: [PATCH 11/26] Fix impl --- .../client/localhost/LocalhostRuleBasedSegmentsStorage.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorage.java b/src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorage.java index 137b1f6b0..468022148 100644 --- a/src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorage.java +++ b/src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorage.java @@ -7,13 +7,12 @@ import io.split.android.client.dtos.RuleBasedSegment; import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; -import io.split.android.engine.experiments.ParsedRuleBasedSegment; public class LocalhostRuleBasedSegmentsStorage implements RuleBasedSegmentStorage { @Nullable @Override - public ParsedRuleBasedSegment get(String segmentName, String matchingKey) { + public RuleBasedSegment get(String segmentName) { return null; } From 724c681550c921d04e53415b036247ef17400bdc Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Thu, 27 Feb 2025 14:53:54 -0300 Subject: [PATCH 12/26] Storage change & matcher implementation --- .../LocalhostRuleBasedSegmentsStorage.java | 3 +- .../storage/rbs/RuleBasedSegmentStorage.java | 3 +- .../rbs/RuleBasedSegmentStorageImpl.java | 16 ++++-- .../matchers/InRuleBasedSegmentMatcher.java | 52 +++++++++++++++++++ .../rbs/RuleBasedSegmentStorageImplTest.java | 34 +++++++----- 5 files changed, 88 insertions(+), 20 deletions(-) create mode 100644 src/main/java/io/split/android/engine/matchers/InRuleBasedSegmentMatcher.java diff --git a/src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorage.java b/src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorage.java index 468022148..137b1f6b0 100644 --- a/src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorage.java +++ b/src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorage.java @@ -7,12 +7,13 @@ import io.split.android.client.dtos.RuleBasedSegment; import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; +import io.split.android.engine.experiments.ParsedRuleBasedSegment; public class LocalhostRuleBasedSegmentsStorage implements RuleBasedSegmentStorage { @Nullable @Override - public RuleBasedSegment get(String segmentName) { + public ParsedRuleBasedSegment get(String segmentName, String matchingKey) { return null; } diff --git a/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorage.java b/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorage.java index e71b74eec..baad5d2c6 100644 --- a/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorage.java +++ b/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorage.java @@ -7,11 +7,12 @@ import io.split.android.client.dtos.RuleBasedSegment; import io.split.android.client.storage.RolloutDefinitionsCache; +import io.split.android.engine.experiments.ParsedRuleBasedSegment; public interface RuleBasedSegmentStorage extends RolloutDefinitionsCache { @Nullable - RuleBasedSegment get(String segmentName); + ParsedRuleBasedSegment get(String segmentName, String matchingKey); boolean update(@NonNull Set toAdd, @NonNull Set toRemove, long changeNumber); diff --git a/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImpl.java b/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImpl.java index 80b5759b3..68f24d2c0 100644 --- a/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImpl.java +++ b/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImpl.java @@ -11,23 +11,31 @@ import java.util.concurrent.ConcurrentHashMap; import io.split.android.client.dtos.RuleBasedSegment; +import io.split.android.engine.experiments.ParsedRuleBasedSegment; +import io.split.android.engine.experiments.RuleBasedSegmentParser; public class RuleBasedSegmentStorageImpl implements RuleBasedSegmentStorage { private final ConcurrentHashMap mInMemorySegments; + private final RuleBasedSegmentParser mParser; private final PersistentRuleBasedSegmentStorage mPersistentStorage; private volatile long mChangeNumber; - public RuleBasedSegmentStorageImpl(@NonNull PersistentRuleBasedSegmentStorage persistentStorage) { + public RuleBasedSegmentStorageImpl(@NonNull PersistentRuleBasedSegmentStorage persistentStorage, @NonNull RuleBasedSegmentParser parser) { mInMemorySegments = new ConcurrentHashMap<>(); + mParser = checkNotNull(parser); mPersistentStorage = checkNotNull(persistentStorage); mChangeNumber = -1; } - @Nullable @Override - public RuleBasedSegment get(String segmentName) { - return mInMemorySegments.get(segmentName); + public @Nullable ParsedRuleBasedSegment get(String segmentName, String matchingKey) { + RuleBasedSegment ruleBasedSegment = mInMemorySegments.get(segmentName); + if (ruleBasedSegment == null) { + return null; + } + + return mParser.parse(ruleBasedSegment, matchingKey); } @Override diff --git a/src/main/java/io/split/android/engine/matchers/InRuleBasedSegmentMatcher.java b/src/main/java/io/split/android/engine/matchers/InRuleBasedSegmentMatcher.java new file mode 100644 index 000000000..8f2412f2c --- /dev/null +++ b/src/main/java/io/split/android/engine/matchers/InRuleBasedSegmentMatcher.java @@ -0,0 +1,52 @@ +package io.split.android.engine.matchers; + +import static io.split.android.client.utils.Utils.checkNotNull; + +import java.util.Map; + +import io.split.android.client.Evaluator; +import io.split.android.client.storage.mysegments.MySegmentsStorage; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; +import io.split.android.engine.experiments.ParsedCondition; +import io.split.android.engine.experiments.ParsedRuleBasedSegment; + +public class InRuleBasedSegmentMatcher implements Matcher { + + private final RuleBasedSegmentStorage mRuleBasedSegmentStorage; + private final MySegmentsStorage mMySegmentsStorage; + private final String mSegmentName; + + public InRuleBasedSegmentMatcher(RuleBasedSegmentStorage ruleBasedSegmentStorage, MySegmentsStorage mySegmentsStorage, String segmentName) { + mRuleBasedSegmentStorage = checkNotNull(ruleBasedSegmentStorage); + mMySegmentsStorage = checkNotNull(mySegmentsStorage); + mSegmentName = checkNotNull(segmentName); + } + + @Override + public boolean match(Object matchValue, String bucketingKey, Map attributes, Evaluator evaluator) { + if (!(matchValue instanceof String)) { + return false; + } + + final String matchingKey = (String) matchValue; + final ParsedRuleBasedSegment parsedRuleBasedSegment = mRuleBasedSegmentStorage.get(mSegmentName, matchingKey); + + if (parsedRuleBasedSegment.getExcludedKeys().contains(matchingKey)) { + return false; + } + + for (String segmentName : parsedRuleBasedSegment.getExcludedSegments()) { + if (mMySegmentsStorage.getAll().contains(segmentName)) { + return false; + } + } + + for (ParsedCondition condition : parsedRuleBasedSegment.getParsedConditions()) { + if (condition.matcher().match(matchingKey, bucketingKey, attributes, evaluator)) { + return true; + } + } + + return false; + } +} diff --git a/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java b/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java index d19b02225..111c93e05 100644 --- a/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java +++ b/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java @@ -22,23 +22,29 @@ import io.split.android.client.dtos.Excluded; import io.split.android.client.dtos.RuleBasedSegment; import io.split.android.client.dtos.Status; +import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; +import io.split.android.engine.experiments.ParsedRuleBasedSegment; +import io.split.android.engine.experiments.ParserCommons; +import io.split.android.engine.experiments.RuleBasedSegmentParser; public class RuleBasedSegmentStorageImplTest { private RuleBasedSegmentStorageImpl storage; private PersistentRuleBasedSegmentStorage mPersistentStorage; + private RuleBasedSegmentParser mParser; @Before public void setUp() { mPersistentStorage = mock(PersistentRuleBasedSegmentStorage.class); - storage = new RuleBasedSegmentStorageImpl(mPersistentStorage); + mParser = new RuleBasedSegmentParser(new ParserCommons(mock(MySegmentsStorageContainer.class), mock(MySegmentsStorageContainer.class), mock(RuleBasedSegmentStorageProvider.class))); + storage = new RuleBasedSegmentStorageImpl(mPersistentStorage, mParser); } @Test public void get() { RuleBasedSegment segment = createRuleBasedSegment("segment1"); storage.update(Set.of(segment), null, 1); - assertNotNull(storage.get("segment1")); + assertNotNull(storage.get("segment1", "matchingKey")); } @Test @@ -51,15 +57,15 @@ public void sequentialUpdate() { toAdd.add(segment2); storage.update(toAdd, null, 2); - assertNotNull(storage.get("segment1")); - assertNotNull(storage.get("segment2")); + assertNotNull(storage.get("segment1", "matchingKey")); + assertNotNull(storage.get("segment2", "matchingKey")); assertEquals(2, storage.getChangeNumber()); Set toRemove = new HashSet<>(); toRemove.add(segment1); storage.update(null, toRemove, 3); - assertNull(storage.get("segment1")); - assertNotNull(storage.get("segment2")); + assertNull(storage.get("segment1", "matchingKey")); + assertNotNull(storage.get("segment2", "matchingKey")); assertEquals(3, storage.getChangeNumber()); } @@ -93,7 +99,7 @@ public void clearRemovesAllSegments() { RuleBasedSegment segment = createRuleBasedSegment("segment1"); storage.update(Set.of(segment), null, 1); storage.clear(); - assertNull(storage.get("segment1")); + assertNull(storage.get("segment1", "matchingKey")); } @Test @@ -126,15 +132,15 @@ public void segmentRemoval() { toAdd.add(segment2); storage.update(toAdd, null, 1); - assertNotNull(storage.get("segment1")); - assertNotNull(storage.get("segment2")); + assertNotNull(storage.get("segment1", "matchingKey")); + assertNotNull(storage.get("segment2", "matchingKey")); Set toRemove = new HashSet<>(); toRemove.add(createRuleBasedSegment("segment1")); storage.update(null, toRemove, 2); - assertNull(storage.get("segment1")); - assertNotNull(storage.get("segment2")); + assertNull(storage.get("segment1", "matchingKey")); + assertNotNull(storage.get("segment2", "matchingKey")); } @Test @@ -143,7 +149,7 @@ public void segmentRemovalOfSameSegment() { Set segments = Collections.singleton(segment1); storage.update(segments, segments, 1); - assertNull(storage.get("segment1")); + assertNull(storage.get("segment1", "matchingKey")); assertEquals(1, storage.getChangeNumber()); } @@ -173,12 +179,12 @@ public void loadLocalPopulatesValues() { when(mPersistentStorage.getSnapshot()).thenReturn(snapshot); long initialCn = storage.getChangeNumber(); - RuleBasedSegment initialSegment1 = storage.get("segment1"); + ParsedRuleBasedSegment initialSegment1 = storage.get("segment1", "matchingKey"); storage.loadLocal(); long finalCn = storage.getChangeNumber(); - RuleBasedSegment finalSegment1 = storage.get("segment1"); + ParsedRuleBasedSegment finalSegment1 = storage.get("segment1", "matchingKey"); assertEquals(-1, initialCn); assertEquals(1, finalCn); From 2d221fb37bf9b1eea1e1461fe769a001eda23d39 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Thu, 27 Feb 2025 15:11:27 -0300 Subject: [PATCH 13/26] Temp parser for RBS storage --- .../client/storage/rbs/RuleBasedSegmentStorageImpl.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImpl.java b/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImpl.java index 68f24d2c0..0a5146303 100644 --- a/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImpl.java +++ b/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImpl.java @@ -12,7 +12,6 @@ import io.split.android.client.dtos.RuleBasedSegment; import io.split.android.engine.experiments.ParsedRuleBasedSegment; -import io.split.android.engine.experiments.RuleBasedSegmentParser; public class RuleBasedSegmentStorageImpl implements RuleBasedSegmentStorage { @@ -100,4 +99,11 @@ public void clear() { mChangeNumber = -1; mPersistentStorage.clear(); } + + // stub class + private static final class RuleBasedSegmentParser { + ParsedRuleBasedSegment parse(RuleBasedSegment segment, String matchingKey) { + return null; + } + } } From 24b8c6b455bd8c872198c4d4d8eaaa77523accfd Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Thu, 27 Feb 2025 15:21:26 -0300 Subject: [PATCH 14/26] Fix --- .../client/storage/rbs/RuleBasedSegmentStorageImpl.java | 2 +- .../storage/rbs/RuleBasedSegmentStorageImplTest.java | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImpl.java b/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImpl.java index 0a5146303..37c7e433f 100644 --- a/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImpl.java +++ b/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImpl.java @@ -101,7 +101,7 @@ public void clear() { } // stub class - private static final class RuleBasedSegmentParser { + static final class RuleBasedSegmentParser { ParsedRuleBasedSegment parse(RuleBasedSegment segment, String matchingKey) { return null; } diff --git a/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java b/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java index 111c93e05..e8d06875e 100644 --- a/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java +++ b/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java @@ -22,22 +22,17 @@ import io.split.android.client.dtos.Excluded; import io.split.android.client.dtos.RuleBasedSegment; import io.split.android.client.dtos.Status; -import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; import io.split.android.engine.experiments.ParsedRuleBasedSegment; -import io.split.android.engine.experiments.ParserCommons; -import io.split.android.engine.experiments.RuleBasedSegmentParser; public class RuleBasedSegmentStorageImplTest { private RuleBasedSegmentStorageImpl storage; private PersistentRuleBasedSegmentStorage mPersistentStorage; - private RuleBasedSegmentParser mParser; @Before public void setUp() { mPersistentStorage = mock(PersistentRuleBasedSegmentStorage.class); - mParser = new RuleBasedSegmentParser(new ParserCommons(mock(MySegmentsStorageContainer.class), mock(MySegmentsStorageContainer.class), mock(RuleBasedSegmentStorageProvider.class))); - storage = new RuleBasedSegmentStorageImpl(mPersistentStorage, mParser); + storage = new RuleBasedSegmentStorageImpl(mPersistentStorage, mock(RuleBasedSegmentStorageImpl.RuleBasedSegmentParser.class)); } @Test From f042de3c8ef905705c982bde2276f2d241412626 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Thu, 27 Feb 2025 15:38:16 -0300 Subject: [PATCH 15/26] Fix test --- .../rbs/RuleBasedSegmentStorageImplTest.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java b/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java index e8d06875e..bec4452fc 100644 --- a/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java +++ b/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java @@ -6,12 +6,15 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.junit.Before; import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import java.util.ArrayList; import java.util.Collections; @@ -28,11 +31,22 @@ public class RuleBasedSegmentStorageImplTest { private RuleBasedSegmentStorageImpl storage; private PersistentRuleBasedSegmentStorage mPersistentStorage; + private RuleBasedSegmentStorageImpl.RuleBasedSegmentParser mParser; @Before public void setUp() { mPersistentStorage = mock(PersistentRuleBasedSegmentStorage.class); - storage = new RuleBasedSegmentStorageImpl(mPersistentStorage, mock(RuleBasedSegmentStorageImpl.RuleBasedSegmentParser.class)); + mParser = mock(RuleBasedSegmentStorageImpl.RuleBasedSegmentParser.class); + when(mParser.parse(any(), any())).thenAnswer(new Answer() { + @Override + public ParsedRuleBasedSegment answer(InvocationOnMock invocation) throws Throwable { + ParsedRuleBasedSegment mockResult = mock(ParsedRuleBasedSegment.class); + when(mockResult.getName()).thenReturn(((RuleBasedSegment) invocation.getArguments()[0]).getName()); + + return mockResult; + } + }); + storage = new RuleBasedSegmentStorageImpl(mPersistentStorage, mParser); } @Test From cfa0c6c8e066eb29e1c4c07c22c29d769bd57981 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Thu, 27 Feb 2025 15:51:49 -0300 Subject: [PATCH 16/26] In RBS matcher test --- .../matchers/InRuleBasedSegmentMatcher.java | 4 + .../InRuleBasedSegmentMatcherTest.java | 193 ++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 src/test/java/io/split/android/engine/matchers/InRuleBasedSegmentMatcherTest.java diff --git a/src/main/java/io/split/android/engine/matchers/InRuleBasedSegmentMatcher.java b/src/main/java/io/split/android/engine/matchers/InRuleBasedSegmentMatcher.java index 8f2412f2c..b4d771e10 100644 --- a/src/main/java/io/split/android/engine/matchers/InRuleBasedSegmentMatcher.java +++ b/src/main/java/io/split/android/engine/matchers/InRuleBasedSegmentMatcher.java @@ -31,6 +31,10 @@ public boolean match(Object matchValue, String bucketingKey, Map final String matchingKey = (String) matchValue; final ParsedRuleBasedSegment parsedRuleBasedSegment = mRuleBasedSegmentStorage.get(mSegmentName, matchingKey); + if (parsedRuleBasedSegment == null) { + return false; + } + if (parsedRuleBasedSegment.getExcludedKeys().contains(matchingKey)) { return false; } diff --git a/src/test/java/io/split/android/engine/matchers/InRuleBasedSegmentMatcherTest.java b/src/test/java/io/split/android/engine/matchers/InRuleBasedSegmentMatcherTest.java new file mode 100644 index 000000000..e2a6d1cc5 --- /dev/null +++ b/src/test/java/io/split/android/engine/matchers/InRuleBasedSegmentMatcherTest.java @@ -0,0 +1,193 @@ +package io.split.android.engine.matchers; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import io.split.android.client.Evaluator; +import io.split.android.client.storage.mysegments.MySegmentsStorage; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; +import io.split.android.engine.experiments.ParsedCondition; +import io.split.android.engine.experiments.ParsedRuleBasedSegment; + +public class InRuleBasedSegmentMatcherTest { + + private static final String SEGMENT_NAME = "test-segment"; + private static final String MATCHING_KEY = "test-key"; + private static final String BUCKETING_KEY = "test-bucketing-key"; + + private RuleBasedSegmentStorage mRuleBasedSegmentStorage; + private MySegmentsStorage mMySegmentsStorage; + private InRuleBasedSegmentMatcher mMatcher; + private Evaluator mEvaluator; + + @Before + public void setUp() { + mRuleBasedSegmentStorage = mock(RuleBasedSegmentStorage.class); + mMySegmentsStorage = mock(MySegmentsStorage.class); + mEvaluator = mock(Evaluator.class); + mMatcher = new InRuleBasedSegmentMatcher(mRuleBasedSegmentStorage, mMySegmentsStorage, SEGMENT_NAME); + } + + @Test + public void matchReturnsFalseWhenMatchValueIsNotString() { + assertFalse(mMatcher.match(123, BUCKETING_KEY, Collections.emptyMap(), mEvaluator)); + assertFalse(mMatcher.match(true, BUCKETING_KEY, Collections.emptyMap(), mEvaluator)); + assertFalse(mMatcher.match(null, BUCKETING_KEY, Collections.emptyMap(), mEvaluator)); + } + + @Test + public void matchReturnsFalseWhenKeyIsExcluded() { + ParsedRuleBasedSegment segment = createSegment( + new HashSet<>(Collections.singletonList(MATCHING_KEY)), + Collections.emptySet(), + Collections.emptyList() + ); + + when(mRuleBasedSegmentStorage.get(eq(SEGMENT_NAME), eq(MATCHING_KEY))).thenReturn(segment); + + assertFalse(mMatcher.match(MATCHING_KEY, BUCKETING_KEY, Collections.emptyMap(), mEvaluator)); + } + + @Test + public void matchReturnsFalseWhenInExcludedSegment() { + String excludedSegment = "excluded-segment"; + Set mySegments = new HashSet<>(Collections.singletonList(excludedSegment)); + + ParsedRuleBasedSegment segment = createSegment( + Collections.emptySet(), + new HashSet<>(Collections.singletonList(excludedSegment)), + Collections.emptyList() + ); + + when(mRuleBasedSegmentStorage.get(eq(SEGMENT_NAME), eq(MATCHING_KEY))).thenReturn(segment); + when(mMySegmentsStorage.getAll()).thenReturn(mySegments); + + assertFalse(mMatcher.match(MATCHING_KEY, BUCKETING_KEY, Collections.emptyMap(), mEvaluator)); + } + + @Test + public void matchReturnsTrueWhenConditionMatches() { + CombiningMatcher conditionMatcher = mock(CombiningMatcher.class); + when(conditionMatcher.match(eq(MATCHING_KEY), eq(BUCKETING_KEY), anyMap(), eq(mEvaluator))).thenReturn(true); + + ParsedCondition condition = mock(ParsedCondition.class); + when(condition.matcher()).thenReturn(conditionMatcher); + + List conditions = Collections.singletonList(condition); + + ParsedRuleBasedSegment segment = createSegment( + Collections.emptySet(), + Collections.emptySet(), + conditions + ); + + when(mRuleBasedSegmentStorage.get(eq(SEGMENT_NAME), eq(MATCHING_KEY))).thenReturn(segment); + + assertTrue(mMatcher.match(MATCHING_KEY, BUCKETING_KEY, Collections.emptyMap(), mEvaluator)); + } + + @Test + public void matchReturnsFalseWhenNoConditionMatches() { + CombiningMatcher conditionMatcher = mock(CombiningMatcher.class); + when(conditionMatcher.match(eq(MATCHING_KEY), eq(BUCKETING_KEY), anyMap(), eq(mEvaluator))).thenReturn(false); + + ParsedCondition condition = mock(ParsedCondition.class); + when(condition.matcher()).thenReturn(conditionMatcher); + + List conditions = Collections.singletonList(condition); + + ParsedRuleBasedSegment segment = createSegment( + Collections.emptySet(), + Collections.emptySet(), + conditions + ); + + when(mRuleBasedSegmentStorage.get(eq(SEGMENT_NAME), eq(MATCHING_KEY))).thenReturn(segment); + + assertFalse(mMatcher.match(MATCHING_KEY, BUCKETING_KEY, Collections.emptyMap(), mEvaluator)); + } + + @Test + public void matchReturnsTrueWhenOneOfMultipleConditionsMatches() { + CombiningMatcher conditionMatcher1 = mock(CombiningMatcher.class); + when(conditionMatcher1.match(eq(MATCHING_KEY), eq(BUCKETING_KEY), anyMap(), eq(mEvaluator))).thenReturn(false); + + CombiningMatcher conditionMatcher2 = mock(CombiningMatcher.class); + when(conditionMatcher2.match(eq(MATCHING_KEY), eq(BUCKETING_KEY), anyMap(), eq(mEvaluator))).thenReturn(true); + + ParsedCondition condition1 = mock(ParsedCondition.class); + when(condition1.matcher()).thenReturn(conditionMatcher1); + + ParsedCondition condition2 = mock(ParsedCondition.class); + when(condition2.matcher()).thenReturn(conditionMatcher2); + + List conditions = Arrays.asList(condition1, condition2); + + ParsedRuleBasedSegment segment = createSegment( + Collections.emptySet(), + Collections.emptySet(), + conditions + ); + + when(mRuleBasedSegmentStorage.get(eq(SEGMENT_NAME), eq(MATCHING_KEY))).thenReturn(segment); + + assertTrue(mMatcher.match(MATCHING_KEY, BUCKETING_KEY, Collections.emptyMap(), mEvaluator)); + } + + @Test + public void matchWithAttributes() { + CombiningMatcher conditionMatcher = mock(CombiningMatcher.class); + Map attributes = new HashMap<>(); + attributes.put("age", 30); + attributes.put("country", "US"); + + when(conditionMatcher.match(eq(MATCHING_KEY), eq(BUCKETING_KEY), eq(attributes), eq(mEvaluator))).thenReturn(true); + + ParsedCondition condition = mock(ParsedCondition.class); + when(condition.matcher()).thenReturn(conditionMatcher); + + List conditions = Collections.singletonList(condition); + + ParsedRuleBasedSegment segment = createSegment( + Collections.emptySet(), + Collections.emptySet(), + conditions + ); + + when(mRuleBasedSegmentStorage.get(eq(SEGMENT_NAME), eq(MATCHING_KEY))).thenReturn(segment); + + assertTrue(mMatcher.match(MATCHING_KEY, BUCKETING_KEY, attributes, mEvaluator)); + } + + @Test + public void matchWhenStorageReturnsNull() { + when(mRuleBasedSegmentStorage.get(eq(SEGMENT_NAME), eq(MATCHING_KEY))).thenReturn(null); + + boolean result = mMatcher.match(MATCHING_KEY, BUCKETING_KEY, Collections.emptyMap(), mEvaluator); + assertFalse(result); + } + + private ParsedRuleBasedSegment createSegment(Set excludedKeys, Set excludedSegments, List conditions) { + ParsedRuleBasedSegment segment = mock(ParsedRuleBasedSegment.class); + when(segment.getExcludedKeys()).thenReturn(excludedKeys); + when(segment.getExcludedSegments()).thenReturn(excludedSegments); + when(segment.getParsedConditions()).thenReturn(conditions != null ? conditions : new ArrayList<>()); + return segment; + } +} From 0cbde4c8fefd0a6e12e848102ccd07a33e97c78d Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Thu, 27 Feb 2025 16:58:31 -0300 Subject: [PATCH 17/26] RBS parser --- .../client/SplitClientFactoryImpl.java | 7 +- .../android/client/SplitFactoryHelper.java | 27 +- .../android/client/SplitFactoryImpl.java | 12 +- .../split/android/client/dtos/Excluded.java | 8 + .../localhost/LocalhostSplitFactory.java | 13 +- .../shared/SplitClientContainerImpl.java | 7 +- .../storage/common/SplitStorageContainer.java | 18 +- .../rbs/RuleBasedSegmentStorageImpl.java | 8 +- .../android/engine/experiments/Parser.java | 9 + .../engine/experiments/ParserCommons.java | 241 ++++++++++++++++++ .../experiments/RuleBasedSegmentParser.java | 47 ++++ .../engine/experiments/SplitParser.java | 211 +-------------- .../client/SplitClientImplBaseTest.java | 13 +- .../android/client/SplitManagerImplTest.java | 10 +- .../android/client/TreatmentManagerTest.java | 8 +- .../rbs/RuleBasedSegmentStorageImplTest.java | 19 +- .../client/utils/SplitClientImplFactory.java | 15 +- .../engine/experiments/EvaluatorTest.java | 8 +- .../RuleBasedSegmentParserTest.java | 199 +++++++++++++++ .../engine/experiments/SplitParserTest.java | 46 +++- .../UnsupportedMatcherSplitParserTest.java | 10 +- 21 files changed, 684 insertions(+), 252 deletions(-) create mode 100644 src/main/java/io/split/android/engine/experiments/Parser.java create mode 100644 src/main/java/io/split/android/engine/experiments/ParserCommons.java create mode 100644 src/main/java/io/split/android/engine/experiments/RuleBasedSegmentParser.java create mode 100644 src/test/java/io/split/android/engine/experiments/RuleBasedSegmentParserTest.java diff --git a/src/main/java/io/split/android/client/SplitClientFactoryImpl.java b/src/main/java/io/split/android/client/SplitClientFactoryImpl.java index 544aab48b..23cab0146 100644 --- a/src/main/java/io/split/android/client/SplitClientFactoryImpl.java +++ b/src/main/java/io/split/android/client/SplitClientFactoryImpl.java @@ -36,7 +36,6 @@ public class SplitClientFactoryImpl implements SplitClientFactory { private final SplitFactory mSplitFactory; private final SplitClientContainer mClientContainer; private final SplitClientConfig mConfig; - private final SyncManager mSyncManager; private final TelemetrySynchronizer mTelemetrySynchronizer; private final SplitStorageContainer mStorageContainer; @@ -58,11 +57,11 @@ public SplitClientFactoryImpl(@NonNull SplitFactory splitFactory, @NonNull KeyValidator keyValidator, @NonNull EventsTracker eventsTracker, @NonNull ImpressionListener.FederatedImpressionListener customerImpressionListener, - @Nullable FlagSetsFilter flagSetsFilter) { + @Nullable FlagSetsFilter flagSetsFilter, + @NonNull SplitParser splitParser) { mSplitFactory = checkNotNull(splitFactory); mClientContainer = checkNotNull(clientContainer); mConfig = checkNotNull(config); - mSyncManager = checkNotNull(syncManager); mStorageContainer = checkNotNull(storageContainer); mTelemetrySynchronizer = checkNotNull(telemetrySynchronizer); @@ -73,7 +72,7 @@ public SplitClientFactoryImpl(@NonNull SplitFactory splitFactory, validationLogger, splitTaskExecutor, mStorageContainer.getPersistentAttributesStorage()); - mSplitParser = new SplitParser(mStorageContainer.getMySegmentsStorageContainer(), mStorageContainer.getMyLargeSegmentsStorageContainer()); + mSplitParser = splitParser; mSplitValidator = new SplitValidatorImpl(); SplitsStorage splitsStorage = mStorageContainer.getSplitsStorage(); mTreatmentManagerFactory = new TreatmentManagerFactoryImpl( diff --git a/src/main/java/io/split/android/client/SplitFactoryHelper.java b/src/main/java/io/split/android/client/SplitFactoryHelper.java index be33b61c3..09959aa64 100644 --- a/src/main/java/io/split/android/client/SplitFactoryHelper.java +++ b/src/main/java/io/split/android/client/SplitFactoryHelper.java @@ -84,7 +84,12 @@ import io.split.android.client.storage.db.SplitRoomDatabase; import io.split.android.client.storage.db.StorageFactory; import io.split.android.client.storage.events.PersistentEventsStorage; +import io.split.android.client.storage.general.GeneralInfoStorage; import io.split.android.client.storage.impressions.PersistentImpressionsStorage; +import io.split.android.client.storage.rbs.LazyRuleBasedSegmentStorageProvider; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorageImpl; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; import io.split.android.client.storage.splits.SplitsStorage; import io.split.android.client.telemetry.TelemetrySynchronizer; import io.split.android.client.telemetry.TelemetrySynchronizerImpl; @@ -93,6 +98,8 @@ import io.split.android.client.telemetry.storage.TelemetryStorage; import io.split.android.client.utils.Utils; import io.split.android.client.utils.logger.Logger; +import io.split.android.engine.experiments.ParserCommons; +import io.split.android.engine.experiments.RuleBasedSegmentParser; class SplitFactoryHelper { private static final int DB_MAGIC_CHARS_COUNT = 4; @@ -169,6 +176,7 @@ SplitStorageContainer buildStorageContainer(UserConsent userConsentStatus, StorageFactory.getPersistentEventsStorage(splitRoomDatabase, splitCipher); PersistentImpressionsStorage persistentImpressionsStorage = StorageFactory.getPersistentImpressionsStorage(splitRoomDatabase, splitCipher); + GeneralInfoStorage generalInfoStorage = StorageFactory.getGeneralInfoStorage(splitRoomDatabase); return new SplitStorageContainer( StorageFactory.getSplitsStorage(splitRoomDatabase, splitCipher), StorageFactory.getMySegmentsStorage(splitRoomDatabase, splitCipher), @@ -184,7 +192,9 @@ SplitStorageContainer buildStorageContainer(UserConsent userConsentStatus, StorageFactory.getPersistentAttributesStorage(splitRoomDatabase, splitCipher), getTelemetryStorage(shouldRecordTelemetry, telemetryStorage), StorageFactory.getImpressionsObserverCachePersistentStorage(splitRoomDatabase, observerCacheExpirationPeriod, impressionsObserverExecutor), - StorageFactory.getGeneralInfoStorage(splitRoomDatabase)); + generalInfoStorage, + StorageFactory.getRuleBasedSegmentStorageProvider(), + StorageFactory.getPersistentRuleBasedSegmentStorage(splitRoomDatabase, splitCipher, generalInfoStorage)); } SplitApiFacade buildApiFacade(SplitClientConfig splitClientConfig, @@ -462,6 +472,21 @@ ExecutorService getImpressionsLoggingTaskExecutor() { new ThreadPoolExecutor.CallerRunsPolicy()); } + @NonNull + static ParserCommons getParserCommons(SplitStorageContainer storageContainer) { + RuleBasedSegmentStorageProvider ruleBasedSegmentStorageProvider = storageContainer.getRuleBasedSegmentStorageProvider(); + ParserCommons parserCommons = new ParserCommons( + storageContainer.getMySegmentsStorageContainer(), + storageContainer.getMyLargeSegmentsStorageContainer(), + ruleBasedSegmentStorageProvider); + RuleBasedSegmentParser ruleBasedSegmentParser = new RuleBasedSegmentParser(parserCommons); + + RuleBasedSegmentStorage ruleBasedSegmentStorage = + new RuleBasedSegmentStorageImpl(storageContainer.getPersistentRuleBasedSegmentStorage(), ruleBasedSegmentParser); + ((LazyRuleBasedSegmentStorageProvider) ruleBasedSegmentStorageProvider).set(ruleBasedSegmentStorage); + return parserCommons; + } + private TelemetryStorage getTelemetryStorage(boolean shouldRecordTelemetry, TelemetryStorage telemetryStorage) { if (telemetryStorage != null) { return telemetryStorage; diff --git a/src/main/java/io/split/android/client/SplitFactoryImpl.java b/src/main/java/io/split/android/client/SplitFactoryImpl.java index ea65577f1..86e055962 100644 --- a/src/main/java/io/split/android/client/SplitFactoryImpl.java +++ b/src/main/java/io/split/android/client/SplitFactoryImpl.java @@ -71,6 +71,7 @@ import io.split.android.client.validators.ValidationErrorInfo; import io.split.android.client.validators.ValidationMessageLogger; import io.split.android.client.validators.ValidationMessageLoggerImpl; +import io.split.android.engine.experiments.ParserCommons; import io.split.android.engine.experiments.SplitParser; public class SplitFactoryImpl implements SplitFactory { @@ -272,12 +273,18 @@ private SplitFactoryImpl(@NonNull String apiToken, @NonNull Key key, @NonNull Sp mEventsManagerCoordinator, mSynchronizer, streamingComponents.getNotificationParser(), streamingComponents.getNotificationProcessor(), streamingComponents.getSseAuthenticator(), mStorageContainer, mSyncManager, compressionProvider); + + ParserCommons parserCommons = SplitFactoryHelper.getParserCommons(mStorageContainer); + + // Create SplitParser with ParserCommons + SplitParser splitParser = new SplitParser(parserCommons); + mClientContainer = new SplitClientContainerImpl( mDefaultClientKey.matchingKey(), this, config, mSyncManager, telemetrySynchronizer, mStorageContainer, splitTaskExecutor, splitApiFacade, validationLogger, keyValidator, customerImpressionListener, streamingComponents.getPushNotificationManager(), componentsRegister, workManagerWrapper, - eventsTracker, flagSetsFilter); + eventsTracker, flagSetsFilter, splitParser); mDestroyer = new Runnable() { public void run() { mInitLock.lock(); @@ -355,10 +362,9 @@ public void run() { // Initialize default client client(); - SplitParser mSplitParser = new SplitParser(mStorageContainer.getMySegmentsStorageContainer(), mStorageContainer.getMyLargeSegmentsStorageContainer()); mManager = new SplitManagerImpl( mStorageContainer.getSplitsStorage(), - new SplitValidatorImpl(), mSplitParser); + new SplitValidatorImpl(), splitParser); } diff --git a/src/main/java/io/split/android/client/dtos/Excluded.java b/src/main/java/io/split/android/client/dtos/Excluded.java index c4f26a618..c7929954d 100644 --- a/src/main/java/io/split/android/client/dtos/Excluded.java +++ b/src/main/java/io/split/android/client/dtos/Excluded.java @@ -2,6 +2,7 @@ import com.google.gson.annotations.SerializedName; +import java.util.HashSet; import java.util.Set; public class Excluded { @@ -19,4 +20,11 @@ public Set getSegments() { public Set getKeys() { return mKeys; } + + public static Excluded createEmpty() { + Excluded excluded = new Excluded(); + excluded.mKeys = new HashSet<>(); + excluded.mSegments = new HashSet<>(); + return excluded; + } } diff --git a/src/main/java/io/split/android/client/localhost/LocalhostSplitFactory.java b/src/main/java/io/split/android/client/localhost/LocalhostSplitFactory.java index 25092466b..5f589f6d4 100644 --- a/src/main/java/io/split/android/client/localhost/LocalhostSplitFactory.java +++ b/src/main/java/io/split/android/client/localhost/LocalhostSplitFactory.java @@ -34,6 +34,7 @@ import io.split.android.client.validators.AttributesValidatorImpl; import io.split.android.client.validators.SplitValidatorImpl; import io.split.android.client.validators.ValidationMessageLoggerImpl; +import io.split.android.engine.experiments.ParserCommons; import io.split.android.engine.experiments.SplitParser; /** @@ -66,7 +67,9 @@ public LocalhostSplitFactory(String key, Context context, EventsManagerCoordinator eventsManagerCoordinator = new EventsManagerCoordinator(); FileStorage fileStorage = new FileStorage(context.getCacheDir(), ServiceConstants.LOCALHOST_FOLDER); SplitsStorage splitsStorage = new LocalhostSplitsStorage(mLocalhostFileName, context, fileStorage, eventsManagerCoordinator); - SplitParser splitParser = new SplitParser(new LocalhostMySegmentsStorageContainer(), new LocalhostMySegmentsStorageContainer()); + + SplitParser splitParser = getSplitParser(); + SplitTaskExecutorImpl taskExecutor = new SplitTaskExecutorImpl(); AttributesManagerFactory attributesManagerFactory = new AttributesManagerFactoryImpl(new AttributesValidatorImpl(), new ValidationMessageLoggerImpl()); @@ -102,6 +105,14 @@ public LocalhostSplitFactory(String key, Context context, Logger.i("Android SDK initialized!"); } + @NonNull + private static SplitParser getSplitParser() { + return new SplitParser(new ParserCommons( + new LocalhostMySegmentsStorageContainer(), + new LocalhostMySegmentsStorageContainer(), + new LocalhostRuleBasedSegmentsStorageProvider(new LocalhostRuleBasedSegmentsStorage()))); + } + @VisibleForTesting LocalhostSplitFactory(@NonNull SplitsStorage splitsStorage, @NonNull SplitParser splitParser, diff --git a/src/main/java/io/split/android/client/shared/SplitClientContainerImpl.java b/src/main/java/io/split/android/client/shared/SplitClientContainerImpl.java index 43b91a593..e52ac60dc 100644 --- a/src/main/java/io/split/android/client/shared/SplitClientContainerImpl.java +++ b/src/main/java/io/split/android/client/shared/SplitClientContainerImpl.java @@ -37,6 +37,7 @@ import io.split.android.client.telemetry.TelemetrySynchronizer; import io.split.android.client.validators.KeyValidator; import io.split.android.client.validators.ValidationMessageLogger; +import io.split.android.engine.experiments.SplitParser; public final class SplitClientContainerImpl extends BaseSplitClientContainer { @@ -74,7 +75,8 @@ public SplitClientContainerImpl(@NonNull String defaultMatchingKey, @NonNull ClientComponentsRegister clientComponentsRegister, @NonNull MySegmentsWorkManagerWrapper workManagerWrapper, @NonNull EventsTracker eventsTracker, - @Nullable FlagSetsFilter flagSetsFilter) { + @Nullable FlagSetsFilter flagSetsFilter, + @NonNull SplitParser splitParser) { mDefaultMatchingKey = checkNotNull(defaultMatchingKey); mPushNotificationManager = pushNotificationManager; mStreamingEnabled = config.streamingEnabled(); @@ -93,7 +95,8 @@ public SplitClientContainerImpl(@NonNull String defaultMatchingKey, keyValidator, eventsTracker, customerImpressionListener, - flagSetsFilter + flagSetsFilter, + splitParser ); mClientComponentsRegister = checkNotNull(clientComponentsRegister); mSplitTaskExecutor = checkNotNull(splitTaskExecutor); diff --git a/src/main/java/io/split/android/client/storage/common/SplitStorageContainer.java b/src/main/java/io/split/android/client/storage/common/SplitStorageContainer.java index da911d720..7205d8283 100644 --- a/src/main/java/io/split/android/client/storage/common/SplitStorageContainer.java +++ b/src/main/java/io/split/android/client/storage/common/SplitStorageContainer.java @@ -17,6 +17,8 @@ import io.split.android.client.storage.impressions.PersistentImpressionsUniqueStorage; import io.split.android.client.storage.mysegments.MySegmentsStorage; import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; +import io.split.android.client.storage.rbs.PersistentRuleBasedSegmentStorage; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; import io.split.android.client.storage.splits.PersistentSplitsStorage; import io.split.android.client.storage.splits.SplitsStorage; import io.split.android.client.telemetry.storage.TelemetryStorage; @@ -38,6 +40,8 @@ public class SplitStorageContainer { private final PersistentImpressionsUniqueStorage mPersistentImpressionsUniqueStorage; private final PersistentImpressionsObserverCacheStorage mPersistentImpressionsObserverCacheStorage; private final GeneralInfoStorage mGeneralInfoStorage; + private final RuleBasedSegmentStorageProvider mRuleBasedSegmentStorageProvider; + private final PersistentRuleBasedSegmentStorage mPersistentRuleBasedSegmentStorage; public SplitStorageContainer(@NonNull SplitsStorage splitStorage, @NonNull MySegmentsStorageContainer mySegmentsStorageContainer, @@ -53,7 +57,9 @@ public SplitStorageContainer(@NonNull SplitsStorage splitStorage, @NonNull PersistentAttributesStorage persistentAttributesStorage, @NonNull TelemetryStorage telemetryStorage, @NonNull PersistentImpressionsObserverCacheStorage persistentImpressionsObserverCacheStorage, - @NonNull GeneralInfoStorage generalInfoStorage) { + @NonNull GeneralInfoStorage generalInfoStorage, + @NonNull RuleBasedSegmentStorageProvider ruleBasedSegmentStorageProvider, + @NonNull PersistentRuleBasedSegmentStorage persistentRuleBasedSegmentStorage) { mSplitStorage = checkNotNull(splitStorage); mMySegmentsStorageContainer = checkNotNull(mySegmentsStorageContainer); @@ -70,6 +76,8 @@ public SplitStorageContainer(@NonNull SplitsStorage splitStorage, mPersistentImpressionsUniqueStorage = checkNotNull(persistentImpressionsUniqueStorage); mPersistentImpressionsObserverCacheStorage = checkNotNull(persistentImpressionsObserverCacheStorage); mGeneralInfoStorage = checkNotNull(generalInfoStorage); + mRuleBasedSegmentStorageProvider = checkNotNull(ruleBasedSegmentStorageProvider); + mPersistentRuleBasedSegmentStorage = checkNotNull(persistentRuleBasedSegmentStorage); } public SplitsStorage getSplitsStorage() { @@ -143,4 +151,12 @@ public PersistentImpressionsObserverCacheStorage getImpressionsObserverCachePers public GeneralInfoStorage getGeneralInfoStorage() { return mGeneralInfoStorage; } + + public RuleBasedSegmentStorageProvider getRuleBasedSegmentStorageProvider() { + return mRuleBasedSegmentStorageProvider; + } + + public PersistentRuleBasedSegmentStorage getPersistentRuleBasedSegmentStorage() { + return mPersistentRuleBasedSegmentStorage; + } } diff --git a/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImpl.java b/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImpl.java index 37c7e433f..68f24d2c0 100644 --- a/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImpl.java +++ b/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImpl.java @@ -12,6 +12,7 @@ import io.split.android.client.dtos.RuleBasedSegment; import io.split.android.engine.experiments.ParsedRuleBasedSegment; +import io.split.android.engine.experiments.RuleBasedSegmentParser; public class RuleBasedSegmentStorageImpl implements RuleBasedSegmentStorage { @@ -99,11 +100,4 @@ public void clear() { mChangeNumber = -1; mPersistentStorage.clear(); } - - // stub class - static final class RuleBasedSegmentParser { - ParsedRuleBasedSegment parse(RuleBasedSegment segment, String matchingKey) { - return null; - } - } } diff --git a/src/main/java/io/split/android/engine/experiments/Parser.java b/src/main/java/io/split/android/engine/experiments/Parser.java new file mode 100644 index 000000000..aaa8cdf74 --- /dev/null +++ b/src/main/java/io/split/android/engine/experiments/Parser.java @@ -0,0 +1,9 @@ +package io.split.android.engine.experiments; + +import androidx.annotation.Nullable; + +interface Parser { + + @Nullable + O parse(I input, String matchingKey); +} diff --git a/src/main/java/io/split/android/engine/experiments/ParserCommons.java b/src/main/java/io/split/android/engine/experiments/ParserCommons.java new file mode 100644 index 000000000..168a9b898 --- /dev/null +++ b/src/main/java/io/split/android/engine/experiments/ParserCommons.java @@ -0,0 +1,241 @@ +package io.split.android.engine.experiments; + +import static io.split.android.client.utils.Utils.checkArgument; +import static io.split.android.client.utils.Utils.checkNotNull; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + +import java.util.ArrayList; +import java.util.List; + +import io.split.android.client.dtos.Condition; +import io.split.android.client.dtos.Matcher; +import io.split.android.client.dtos.MatcherGroup; +import io.split.android.client.dtos.Partition; +import io.split.android.client.storage.mysegments.EmptyMySegmentsStorage; +import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; +import io.split.android.client.utils.logger.Logger; +import io.split.android.engine.matchers.AllKeysMatcher; +import io.split.android.engine.matchers.AttributeMatcher; +import io.split.android.engine.matchers.BetweenMatcher; +import io.split.android.engine.matchers.BooleanMatcher; +import io.split.android.engine.matchers.CombiningMatcher; +import io.split.android.engine.matchers.DependencyMatcher; +import io.split.android.engine.matchers.EqualToMatcher; +import io.split.android.engine.matchers.GreaterThanOrEqualToMatcher; +import io.split.android.engine.matchers.InRuleBasedSegmentMatcher; +import io.split.android.engine.matchers.LessThanOrEqualToMatcher; +import io.split.android.engine.matchers.MySegmentsMatcher; +import io.split.android.engine.matchers.collections.ContainsAllOfSetMatcher; +import io.split.android.engine.matchers.collections.ContainsAnyOfSetMatcher; +import io.split.android.engine.matchers.collections.EqualToSetMatcher; +import io.split.android.engine.matchers.collections.PartOfSetMatcher; +import io.split.android.engine.matchers.semver.BetweenSemverMatcher; +import io.split.android.engine.matchers.semver.EqualToSemverMatcher; +import io.split.android.engine.matchers.semver.GreaterThanOrEqualToSemverMatcher; +import io.split.android.engine.matchers.semver.InListSemverMatcher; +import io.split.android.engine.matchers.semver.LessThanOrEqualToSemverMatcher; +import io.split.android.engine.matchers.strings.ContainsAnyOfMatcher; +import io.split.android.engine.matchers.strings.EndsWithAnyOfMatcher; +import io.split.android.engine.matchers.strings.RegularExpressionMatcher; +import io.split.android.engine.matchers.strings.StartsWithAnyOfMatcher; +import io.split.android.engine.matchers.strings.WhitelistMatcher; + +public class ParserCommons { + + public static final int CONDITIONS_UPPER_LIMIT = 50; + private final MySegmentsStorageContainer mMySegmentsStorageContainer; + private final MySegmentsStorageContainer mMyLargeSegmentsStorageContainer; + private final RuleBasedSegmentStorageProvider mRuleBasedSegmentStorageProvider; + private final DefaultConditionsProvider mDefaultConditionsProvider; + private EmptyMySegmentsStorage mEmptyMySegmentsStorage; + + public ParserCommons(@NonNull MySegmentsStorageContainer mySegmentsStorageContainer, + @NonNull MySegmentsStorageContainer myLargeSegmentsStorageContainer, + @NonNull RuleBasedSegmentStorageProvider ruleBasedSegmentStorageProvider) { + this(mySegmentsStorageContainer, myLargeSegmentsStorageContainer, ruleBasedSegmentStorageProvider, new DefaultConditionsProvider()); + } + + @VisibleForTesting + ParserCommons(@NonNull MySegmentsStorageContainer mySegmentsStorageContainer, + @NonNull MySegmentsStorageContainer myLargeSegmentsStorageContainer, + @NonNull RuleBasedSegmentStorageProvider ruleBasedSegmentStorageProvider, + DefaultConditionsProvider defaultConditionsProvider) { + mMySegmentsStorageContainer = checkNotNull(mySegmentsStorageContainer); + mMyLargeSegmentsStorageContainer = checkNotNull(myLargeSegmentsStorageContainer); + mRuleBasedSegmentStorageProvider = checkNotNull(ruleBasedSegmentStorageProvider); + mDefaultConditionsProvider = checkNotNull(defaultConditionsProvider); + } + + @Nullable + List getParsedConditions(String matchingKey, List conditions, String largeConditionSizeMessage) { + if (conditions.size() > CONDITIONS_UPPER_LIMIT) { + Logger.w(largeConditionSizeMessage); + return null; + } + + List parsedConditionList = new ArrayList<>(); + + try { + for (Condition condition : conditions) { + List partitions = condition.partitions; + CombiningMatcher matcher = toMatcher(condition.matcherGroup, matchingKey); + parsedConditionList.add(new ParsedCondition(condition.conditionType, matcher, partitions, condition.label)); + } + } catch (UnsupportedMatcherException e) { + Logger.w(e.getMessage()); + parsedConditionList = mDefaultConditionsProvider.getDefaultConditions(); + } + return parsedConditionList; + } + + private CombiningMatcher toMatcher(MatcherGroup matcherGroup, String matchingKey) throws UnsupportedMatcherException { + List matchers = matcherGroup.matchers; + checkArgument(!matchers.isEmpty()); + + List toCombine = new ArrayList<>(); + + for (Matcher matcher : matchers) { + AttributeMatcher attributeMatcher = toMatcher(matcher, matchingKey); + + toCombine.add(attributeMatcher); + } + + return new CombiningMatcher(matcherGroup.combiner, toCombine); + } + + private AttributeMatcher toMatcher(Matcher matcher, String matchingKey) throws UnsupportedMatcherException { + io.split.android.engine.matchers.Matcher delegate; + + // Values not present in {@link io.split.android.client.dtos.MatcherType} are deserialized as null + if (matcher.matcherType == null) { + throw new UnsupportedMatcherException("Unable to create matcher for matcher type"); + } + + switch (matcher.matcherType) { + case ALL_KEYS: + delegate = new AllKeysMatcher(); + break; + case IN_SEGMENT: + checkNotNull(matcher.userDefinedSegmentMatcherData); + delegate = new MySegmentsMatcher(matchingKey != null ? mMySegmentsStorageContainer.getStorageForKey(matchingKey) : getEmptyMySegmentsStorage(), + matcher.userDefinedSegmentMatcherData.segmentName); + break; + case IN_LARGE_SEGMENT: + checkNotNull(matcher.userDefinedLargeSegmentMatcherData); + delegate = new MySegmentsMatcher((matchingKey != null) ? mMyLargeSegmentsStorageContainer.getStorageForKey(matchingKey) : getEmptyMySegmentsStorage(), + matcher.userDefinedLargeSegmentMatcherData.largeSegmentName); + break; + case WHITELIST: + checkNotNull(matcher.whitelistMatcherData); + delegate = new WhitelistMatcher(matcher.whitelistMatcherData.whitelist); + break; + case EQUAL_TO: + checkNotNull(matcher.unaryNumericMatcherData); + delegate = new EqualToMatcher(matcher.unaryNumericMatcherData.value, matcher.unaryNumericMatcherData.dataType); + break; + case GREATER_THAN_OR_EQUAL_TO: + checkNotNull(matcher.unaryNumericMatcherData); + delegate = new GreaterThanOrEqualToMatcher(matcher.unaryNumericMatcherData.value, matcher.unaryNumericMatcherData.dataType); + break; + case LESS_THAN_OR_EQUAL_TO: + checkNotNull(matcher.unaryNumericMatcherData); + delegate = new LessThanOrEqualToMatcher(matcher.unaryNumericMatcherData.value, matcher.unaryNumericMatcherData.dataType); + break; + case BETWEEN: + checkNotNull(matcher.betweenMatcherData); + delegate = new BetweenMatcher(matcher.betweenMatcherData.start, matcher.betweenMatcherData.end, matcher.betweenMatcherData.dataType); + break; + case EQUAL_TO_SET: + checkNotNull(matcher.whitelistMatcherData); + delegate = new EqualToSetMatcher(matcher.whitelistMatcherData.whitelist); + break; + case PART_OF_SET: + checkNotNull(matcher.whitelistMatcherData); + delegate = new PartOfSetMatcher(matcher.whitelistMatcherData.whitelist); + break; + case CONTAINS_ALL_OF_SET: + checkNotNull(matcher.whitelistMatcherData); + delegate = new ContainsAllOfSetMatcher(matcher.whitelistMatcherData.whitelist); + break; + case CONTAINS_ANY_OF_SET: + checkNotNull(matcher.whitelistMatcherData); + delegate = new ContainsAnyOfSetMatcher(matcher.whitelistMatcherData.whitelist); + break; + case STARTS_WITH: + checkNotNull(matcher.whitelistMatcherData); + delegate = new StartsWithAnyOfMatcher(matcher.whitelistMatcherData.whitelist); + break; + case ENDS_WITH: + checkNotNull(matcher.whitelistMatcherData); + delegate = new EndsWithAnyOfMatcher(matcher.whitelistMatcherData.whitelist); + break; + case CONTAINS_STRING: + checkNotNull(matcher.whitelistMatcherData); + delegate = new ContainsAnyOfMatcher(matcher.whitelistMatcherData.whitelist); + break; + case MATCHES_STRING: + checkNotNull(matcher.stringMatcherData); + delegate = new RegularExpressionMatcher(matcher.stringMatcherData); + break; + case IN_SPLIT_TREATMENT: + checkNotNull(matcher.dependencyMatcherData, + "MatcherType is " + matcher.matcherType + + ". matcher.dependencyMatcherData() MUST NOT BE null"); + delegate = new DependencyMatcher(matcher.dependencyMatcherData.split, matcher.dependencyMatcherData.treatments); + break; + case EQUAL_TO_BOOLEAN: + checkNotNull(matcher.booleanMatcherData, + "MatcherType is " + matcher.matcherType + + ". matcher.booleanMatcherData() MUST NOT BE null"); + delegate = new BooleanMatcher(matcher.booleanMatcherData); + break; + case EQUAL_TO_SEMVER: + delegate = new EqualToSemverMatcher(matcher.stringMatcherData); + break; + case GREATER_THAN_OR_EQUAL_TO_SEMVER: + delegate = new GreaterThanOrEqualToSemverMatcher(matcher.stringMatcherData); + break; + case LESS_THAN_OR_EQUAL_TO_SEMVER: + delegate = new LessThanOrEqualToSemverMatcher(matcher.stringMatcherData); + break; + case BETWEEN_SEMVER: + delegate = new BetweenSemverMatcher(matcher.betweenStringMatcherData.start, matcher.betweenStringMatcherData.end); + break; + case IN_LIST_SEMVER: + delegate = new InListSemverMatcher(matcher.whitelistMatcherData.whitelist); + break; + case IN_RULE_BASED_SEGMENT: + delegate = new InRuleBasedSegmentMatcher(mRuleBasedSegmentStorageProvider.get(), + (matchingKey != null) ? mMySegmentsStorageContainer.getStorageForKey(matchingKey) : getEmptyMySegmentsStorage(), + matcher.userDefinedSegmentMatcherData.segmentName); + break; + default: + // since values not present in {@link io.split.android.client.dtos.MatcherType} + // are deserialized as null, this would most likely not be reached. Adding it for completeness + throw new UnsupportedMatcherException("Unable to create matcher for matcher type: " + matcher.matcherType); + } + + String attribute = null; + if (matcher.keySelector != null && matcher.keySelector.attribute != null) { + attribute = matcher.keySelector.attribute; + } + + boolean negate = matcher.negate; + + + return new AttributeMatcher(attribute, delegate, negate); + } + + @NonNull + private EmptyMySegmentsStorage getEmptyMySegmentsStorage() { + if (mEmptyMySegmentsStorage == null) { + mEmptyMySegmentsStorage = new EmptyMySegmentsStorage(); + } + + return mEmptyMySegmentsStorage; + } +} diff --git a/src/main/java/io/split/android/engine/experiments/RuleBasedSegmentParser.java b/src/main/java/io/split/android/engine/experiments/RuleBasedSegmentParser.java new file mode 100644 index 000000000..e3a223c96 --- /dev/null +++ b/src/main/java/io/split/android/engine/experiments/RuleBasedSegmentParser.java @@ -0,0 +1,47 @@ +package io.split.android.engine.experiments; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; + +import io.split.android.client.dtos.Condition; +import io.split.android.client.dtos.Excluded; +import io.split.android.client.dtos.RuleBasedSegment; + +public class RuleBasedSegmentParser implements Parser { + + private final ParserCommons mParserCommons; + + public RuleBasedSegmentParser(@NonNull ParserCommons parserCommons) { + mParserCommons = parserCommons; + } + + @Nullable + @Override + public ParsedRuleBasedSegment parse(@NonNull RuleBasedSegment input, String matchingKey) { + String name = input.getName(); + Excluded excluded = input.getExcluded(); + List conditions = input.getConditions(); + List parsedConditions = mParserCommons.getParsedConditions( + matchingKey, + conditions, + "Dropping rule based segment name=" + name + " due to large number of conditions (" + conditions.size() + ")"); + + if (parsedConditions == null) { + parsedConditions = new ArrayList<>(); + } + + if (excluded == null) { + excluded = Excluded.createEmpty(); + } + + return new ParsedRuleBasedSegment(name, + excluded.getKeys(), + excluded.getSegments(), + parsedConditions, + input.getTrafficTypeName(), + input.getChangeNumber()); + } +} diff --git a/src/main/java/io/split/android/engine/experiments/SplitParser.java b/src/main/java/io/split/android/engine/experiments/SplitParser.java index e87b2b459..03cbc27e4 100644 --- a/src/main/java/io/split/android/engine/experiments/SplitParser.java +++ b/src/main/java/io/split/android/engine/experiments/SplitParser.java @@ -1,73 +1,21 @@ package io.split.android.engine.experiments; -import static io.split.android.client.utils.Utils.checkArgument; import static io.split.android.client.utils.Utils.checkNotNull; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; -import java.util.ArrayList; import java.util.List; -import io.split.android.client.dtos.Condition; -import io.split.android.client.dtos.Matcher; -import io.split.android.client.dtos.MatcherGroup; -import io.split.android.client.dtos.Partition; import io.split.android.client.dtos.Split; import io.split.android.client.dtos.Status; -import io.split.android.client.storage.mysegments.EmptyMySegmentsStorage; -import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; import io.split.android.client.utils.logger.Logger; -import io.split.android.engine.matchers.AllKeysMatcher; -import io.split.android.engine.matchers.AttributeMatcher; -import io.split.android.engine.matchers.BetweenMatcher; -import io.split.android.engine.matchers.BooleanMatcher; -import io.split.android.engine.matchers.CombiningMatcher; -import io.split.android.engine.matchers.DependencyMatcher; -import io.split.android.engine.matchers.EqualToMatcher; -import io.split.android.engine.matchers.GreaterThanOrEqualToMatcher; -import io.split.android.engine.matchers.LessThanOrEqualToMatcher; -import io.split.android.engine.matchers.MySegmentsMatcher; -import io.split.android.engine.matchers.collections.ContainsAllOfSetMatcher; -import io.split.android.engine.matchers.collections.ContainsAnyOfSetMatcher; -import io.split.android.engine.matchers.collections.EqualToSetMatcher; -import io.split.android.engine.matchers.collections.PartOfSetMatcher; -import io.split.android.engine.matchers.semver.BetweenSemverMatcher; -import io.split.android.engine.matchers.semver.EqualToSemverMatcher; -import io.split.android.engine.matchers.semver.GreaterThanOrEqualToSemverMatcher; -import io.split.android.engine.matchers.semver.InListSemverMatcher; -import io.split.android.engine.matchers.semver.LessThanOrEqualToSemverMatcher; -import io.split.android.engine.matchers.strings.ContainsAnyOfMatcher; -import io.split.android.engine.matchers.strings.EndsWithAnyOfMatcher; -import io.split.android.engine.matchers.strings.RegularExpressionMatcher; -import io.split.android.engine.matchers.strings.StartsWithAnyOfMatcher; -import io.split.android.engine.matchers.strings.WhitelistMatcher; -public class SplitParser { +public class SplitParser implements Parser { - public static final int CONDITIONS_UPPER_LIMIT = 50; + private final ParserCommons mParserCommons; - private final MySegmentsStorageContainer mMySegmentsStorageContainer; - private final MySegmentsStorageContainer mMyLargeSegmentsStorageContainer; - private final DefaultConditionsProvider mDefaultConditionsProvider; - - public SplitParser(@NonNull MySegmentsStorageContainer mySegmentsStorageContainer, @Nullable MySegmentsStorageContainer myLargeSegmentsStorageContainer) { - this(mySegmentsStorageContainer, myLargeSegmentsStorageContainer, new DefaultConditionsProvider()); - } - - @VisibleForTesting - static SplitParser get(MySegmentsStorageContainer mySegmentsStorageContainer, MySegmentsStorageContainer myLargeSegmentsStorageContainer) { - return new SplitParser(mySegmentsStorageContainer, myLargeSegmentsStorageContainer); - } - - @VisibleForTesting - SplitParser(@NonNull MySegmentsStorageContainer mySegmentsStorageContainer, - @Nullable MySegmentsStorageContainer myLargeSegmentsStorageContainer, - @NonNull DefaultConditionsProvider defaultConditionsProvider) { - mMySegmentsStorageContainer = checkNotNull(mySegmentsStorageContainer); - mMyLargeSegmentsStorageContainer = myLargeSegmentsStorageContainer; - mDefaultConditionsProvider = checkNotNull(defaultConditionsProvider); + public SplitParser(ParserCommons parserCommons) { + mParserCommons = checkNotNull(parserCommons); } @Nullable @@ -75,6 +23,7 @@ public ParsedSplit parse(@Nullable Split split) { return parse(split, null); } + @Override @Nullable public ParsedSplit parse(@Nullable Split split, @Nullable String matchingKey) { try { @@ -94,25 +43,12 @@ private ParsedSplit parseWithoutExceptionHandling(Split split, String matchingKe return null; } - if (split.conditions.size() > CONDITIONS_UPPER_LIMIT) { - Logger.w("Dropping feature flag name=%s due to large number of conditions(%d)", - split.name, split.conditions.size()); + List parsedConditionList = mParserCommons.getParsedConditions(matchingKey, split.conditions, + "Dropping feature flag name=" + split.name + " due to large number of conditions (" + split.conditions.size() + ")"); + if (parsedConditionList == null) { return null; } - List parsedConditionList = new ArrayList<>(); - - try { - for (Condition condition : split.conditions) { - List partitions = condition.partitions; - CombiningMatcher matcher = toMatcher(condition.matcherGroup, matchingKey); - parsedConditionList.add(new ParsedCondition(condition.conditionType, matcher, partitions, condition.label)); - } - } catch (UnsupportedMatcherException e) { - Logger.w(e.getMessage()); - parsedConditionList = mDefaultConditionsProvider.getDefaultConditions(); - } - return new ParsedSplit(split.name, split.seed, split.killed, @@ -127,135 +63,4 @@ private ParsedSplit parseWithoutExceptionHandling(Split split, String matchingKe split.sets, split.impressionsDisabled); } - - private CombiningMatcher toMatcher(MatcherGroup matcherGroup, String matchingKey) throws UnsupportedMatcherException { - List matchers = matcherGroup.matchers; - checkArgument(!matchers.isEmpty()); - - List toCombine = new ArrayList<>(); - - for (Matcher matcher : matchers) { - AttributeMatcher attributeMatcher = toMatcher(matcher, matchingKey); - - toCombine.add(attributeMatcher); - } - - return new CombiningMatcher(matcherGroup.combiner, toCombine); - } - - private AttributeMatcher toMatcher(Matcher matcher, String matchingKey) throws UnsupportedMatcherException { - io.split.android.engine.matchers.Matcher delegate; - - // Values not present in {@link io.split.android.client.dtos.MatcherType} are deserialized as null - if (matcher.matcherType == null) { - throw new UnsupportedMatcherException("Unable to create matcher for matcher type"); - } - - switch (matcher.matcherType) { - case ALL_KEYS: - delegate = new AllKeysMatcher(); - break; - case IN_SEGMENT: - checkNotNull(matcher.userDefinedSegmentMatcherData); - delegate = new MySegmentsMatcher((matchingKey != null) ? mMySegmentsStorageContainer.getStorageForKey(matchingKey) : new EmptyMySegmentsStorage(), matcher.userDefinedSegmentMatcherData.segmentName); - break; - case IN_LARGE_SEGMENT: - checkNotNull(matcher.userDefinedLargeSegmentMatcherData); - delegate = new MySegmentsMatcher((matchingKey != null) ? mMyLargeSegmentsStorageContainer.getStorageForKey(matchingKey) : new EmptyMySegmentsStorage(), matcher.userDefinedLargeSegmentMatcherData.largeSegmentName); - break; - case WHITELIST: - checkNotNull(matcher.whitelistMatcherData); - delegate = new WhitelistMatcher(matcher.whitelistMatcherData.whitelist); - break; - case EQUAL_TO: - checkNotNull(matcher.unaryNumericMatcherData); - delegate = new EqualToMatcher(matcher.unaryNumericMatcherData.value, matcher.unaryNumericMatcherData.dataType); - break; - case GREATER_THAN_OR_EQUAL_TO: - checkNotNull(matcher.unaryNumericMatcherData); - delegate = new GreaterThanOrEqualToMatcher(matcher.unaryNumericMatcherData.value, matcher.unaryNumericMatcherData.dataType); - break; - case LESS_THAN_OR_EQUAL_TO: - checkNotNull(matcher.unaryNumericMatcherData); - delegate = new LessThanOrEqualToMatcher(matcher.unaryNumericMatcherData.value, matcher.unaryNumericMatcherData.dataType); - break; - case BETWEEN: - checkNotNull(matcher.betweenMatcherData); - delegate = new BetweenMatcher(matcher.betweenMatcherData.start, matcher.betweenMatcherData.end, matcher.betweenMatcherData.dataType); - break; - case EQUAL_TO_SET: - checkNotNull(matcher.whitelistMatcherData); - delegate = new EqualToSetMatcher(matcher.whitelistMatcherData.whitelist); - break; - case PART_OF_SET: - checkNotNull(matcher.whitelistMatcherData); - delegate = new PartOfSetMatcher(matcher.whitelistMatcherData.whitelist); - break; - case CONTAINS_ALL_OF_SET: - checkNotNull(matcher.whitelistMatcherData); - delegate = new ContainsAllOfSetMatcher(matcher.whitelistMatcherData.whitelist); - break; - case CONTAINS_ANY_OF_SET: - checkNotNull(matcher.whitelistMatcherData); - delegate = new ContainsAnyOfSetMatcher(matcher.whitelistMatcherData.whitelist); - break; - case STARTS_WITH: - checkNotNull(matcher.whitelistMatcherData); - delegate = new StartsWithAnyOfMatcher(matcher.whitelistMatcherData.whitelist); - break; - case ENDS_WITH: - checkNotNull(matcher.whitelistMatcherData); - delegate = new EndsWithAnyOfMatcher(matcher.whitelistMatcherData.whitelist); - break; - case CONTAINS_STRING: - checkNotNull(matcher.whitelistMatcherData); - delegate = new ContainsAnyOfMatcher(matcher.whitelistMatcherData.whitelist); - break; - case MATCHES_STRING: - checkNotNull(matcher.stringMatcherData); - delegate = new RegularExpressionMatcher(matcher.stringMatcherData); - break; - case IN_SPLIT_TREATMENT: - checkNotNull(matcher.dependencyMatcherData, - "MatcherType is " + matcher.matcherType - + ". matcher.dependencyMatcherData() MUST NOT BE null"); - delegate = new DependencyMatcher(matcher.dependencyMatcherData.split, matcher.dependencyMatcherData.treatments); - break; - case EQUAL_TO_BOOLEAN: - checkNotNull(matcher.booleanMatcherData, - "MatcherType is " + matcher.matcherType - + ". matcher.booleanMatcherData() MUST NOT BE null"); - delegate = new BooleanMatcher(matcher.booleanMatcherData); - break; - case EQUAL_TO_SEMVER: - delegate = new EqualToSemverMatcher(matcher.stringMatcherData); - break; - case GREATER_THAN_OR_EQUAL_TO_SEMVER: - delegate = new GreaterThanOrEqualToSemverMatcher(matcher.stringMatcherData); - break; - case LESS_THAN_OR_EQUAL_TO_SEMVER: - delegate = new LessThanOrEqualToSemverMatcher(matcher.stringMatcherData); - break; - case BETWEEN_SEMVER: - delegate = new BetweenSemverMatcher(matcher.betweenStringMatcherData.start, matcher.betweenStringMatcherData.end); - break; - case IN_LIST_SEMVER: - delegate = new InListSemverMatcher(matcher.whitelistMatcherData.whitelist); - break; - default: - // since values not present in {@link io.split.android.client.dtos.MatcherType} - // are deserialized as null, this would most likely not be reached. Adding it for completeness - throw new UnsupportedMatcherException("Unable to create matcher for matcher type: " + matcher.matcherType); - } - - String attribute = null; - if (matcher.keySelector != null && matcher.keySelector.attribute != null) { - attribute = matcher.keySelector.attribute; - } - - boolean negate = matcher.negate; - - - return new AttributeMatcher(attribute, delegate, negate); - } } diff --git a/src/test/java/io/split/android/client/SplitClientImplBaseTest.java b/src/test/java/io/split/android/client/SplitClientImplBaseTest.java index 7b8076a18..e0b8fc56f 100644 --- a/src/test/java/io/split/android/client/SplitClientImplBaseTest.java +++ b/src/test/java/io/split/android/client/SplitClientImplBaseTest.java @@ -1,5 +1,7 @@ package io.split.android.client; +import static org.mockito.Mockito.when; + import org.junit.Before; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -12,10 +14,12 @@ import io.split.android.client.shared.SplitClientContainer; import io.split.android.client.storage.mysegments.MySegmentsStorage; import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; import io.split.android.client.storage.splits.SplitsStorage; -import io.split.android.client.telemetry.storage.TelemetryStorageProducer; import io.split.android.client.validators.SplitValidator; import io.split.android.client.validators.TreatmentManager; +import io.split.android.engine.experiments.ParserCommons; import io.split.android.engine.experiments.SplitParser; import io.split.android.fake.SplitTaskExecutorStub; @@ -32,6 +36,10 @@ public abstract class SplitClientImplBaseTest { @Mock protected MySegmentsStorageContainer myLargeSegmentsStorageContainer; @Mock + protected RuleBasedSegmentStorage ruleBasedSegmentStorage; + @Mock + protected RuleBasedSegmentStorageProvider ruleBasedSegmentStorageProvider; + @Mock protected MySegmentsStorage mySegmentsStorage; @Mock protected ImpressionListener impressionListener; @@ -53,12 +61,13 @@ public void setUp() { MockitoAnnotations.openMocks(this); SplitClientConfig splitClientConfig = SplitClientConfig.builder().build(); + when(ruleBasedSegmentStorageProvider.get()).thenReturn(ruleBasedSegmentStorage); splitClient = new SplitClientImpl( container, clientContainer, new Key("test_key"), - new SplitParser(mySegmentsStorageContainer, myLargeSegmentsStorageContainer), + new SplitParser(new ParserCommons(mySegmentsStorageContainer, myLargeSegmentsStorageContainer, ruleBasedSegmentStorageProvider)), impressionListener, splitClientConfig, new SplitEventsManager(splitClientConfig, new SplitTaskExecutorStub()), diff --git a/src/test/java/io/split/android/client/SplitManagerImplTest.java b/src/test/java/io/split/android/client/SplitManagerImplTest.java index 3340701ee..752ec1feb 100644 --- a/src/test/java/io/split/android/client/SplitManagerImplTest.java +++ b/src/test/java/io/split/android/client/SplitManagerImplTest.java @@ -27,10 +27,13 @@ import io.split.android.client.dtos.Split; import io.split.android.client.storage.mysegments.MySegmentsStorage; import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; import io.split.android.client.storage.splits.SplitsStorage; import io.split.android.client.validators.SplitValidator; import io.split.android.client.validators.SplitValidatorImpl; import io.split.android.engine.ConditionsTestUtil; +import io.split.android.engine.experiments.ParserCommons; import io.split.android.engine.experiments.SplitFetcher; import io.split.android.engine.experiments.SplitParser; import io.split.android.engine.matchers.AllKeysMatcher; @@ -48,6 +51,10 @@ public class SplitManagerImplTest { @Mock MySegmentsStorageContainer mMyLargeSegmentsStorageContainer; @Mock + RuleBasedSegmentStorage mRuleBasedSegmentStorage; + @Mock + RuleBasedSegmentStorageProvider mRuleBasedSegmentStorageProvider; + @Mock SplitManager mSplitManager; @Before @@ -55,7 +62,8 @@ public void setup() { MockitoAnnotations.openMocks(this); SplitValidator validator = new SplitValidatorImpl(); when(mMySegmentsStorageContainer.getStorageForKey("")).thenReturn(mMySegmentsStorage); - SplitParser parser = new SplitParser(mMySegmentsStorageContainer, mMyLargeSegmentsStorageContainer); + when(mRuleBasedSegmentStorageProvider.get()).thenReturn(mRuleBasedSegmentStorage); + SplitParser parser = new SplitParser(new ParserCommons(mMySegmentsStorageContainer, mMyLargeSegmentsStorageContainer, mRuleBasedSegmentStorageProvider)); mSplitManager = new SplitManagerImpl(mSplitsStorage, validator, parser); } diff --git a/src/test/java/io/split/android/client/TreatmentManagerTest.java b/src/test/java/io/split/android/client/TreatmentManagerTest.java index db68254ca..13b8fe6aa 100644 --- a/src/test/java/io/split/android/client/TreatmentManagerTest.java +++ b/src/test/java/io/split/android/client/TreatmentManagerTest.java @@ -32,6 +32,8 @@ import io.split.android.client.impressions.ImpressionListener; import io.split.android.client.storage.mysegments.MySegmentsStorage; import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; import io.split.android.client.storage.splits.SplitsStorage; import io.split.android.client.telemetry.storage.TelemetryStorageProducer; import io.split.android.client.utils.Utils; @@ -44,6 +46,7 @@ import io.split.android.client.validators.TreatmentManagerImpl; import io.split.android.client.validators.ValidationMessageLogger; import io.split.android.client.validators.ValidationMessageLoggerImpl; +import io.split.android.engine.experiments.ParserCommons; import io.split.android.engine.experiments.SplitParser; import io.split.android.fake.SplitEventsManagerStub; import io.split.android.grammar.Treatments; @@ -73,11 +76,14 @@ public void loadSplitsFromFile() { MySegmentsStorageContainer mySegmentsStorageContainer = mock(MySegmentsStorageContainer.class); MySegmentsStorageContainer myLargeSegmentsStorageContainer = mock(MySegmentsStorageContainer.class); MySegmentsStorage mySegmentsStorage = mock(MySegmentsStorage.class); + RuleBasedSegmentStorage ruleBasedSegmentStorage = mock(RuleBasedSegmentStorage.class); + RuleBasedSegmentStorageProvider ruleBasedSegmentStorageProvider = mock(RuleBasedSegmentStorageProvider.class); + when(ruleBasedSegmentStorageProvider.get()).thenReturn(ruleBasedSegmentStorage); SplitsStorage splitsStorage = mock(SplitsStorage.class); Set mySegments = new HashSet(Arrays.asList("s1", "s2", "test_copy")); List splits = fileHelper.loadAndParseSplitChangeFile("split_changes_1.json"); - SplitParser splitParser = new SplitParser(mySegmentsStorageContainer, myLargeSegmentsStorageContainer); + SplitParser splitParser = new SplitParser(new ParserCommons(mySegmentsStorageContainer, myLargeSegmentsStorageContainer, ruleBasedSegmentStorageProvider)); Map splitsMap = splitsMap(splits); when(splitsStorage.getAll()).thenReturn(splitsMap); diff --git a/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java b/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java index bec4452fc..111c93e05 100644 --- a/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java +++ b/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java @@ -6,15 +6,12 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.junit.Before; import org.junit.Test; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; import java.util.ArrayList; import java.util.Collections; @@ -25,27 +22,21 @@ import io.split.android.client.dtos.Excluded; import io.split.android.client.dtos.RuleBasedSegment; import io.split.android.client.dtos.Status; +import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; import io.split.android.engine.experiments.ParsedRuleBasedSegment; +import io.split.android.engine.experiments.ParserCommons; +import io.split.android.engine.experiments.RuleBasedSegmentParser; public class RuleBasedSegmentStorageImplTest { private RuleBasedSegmentStorageImpl storage; private PersistentRuleBasedSegmentStorage mPersistentStorage; - private RuleBasedSegmentStorageImpl.RuleBasedSegmentParser mParser; + private RuleBasedSegmentParser mParser; @Before public void setUp() { mPersistentStorage = mock(PersistentRuleBasedSegmentStorage.class); - mParser = mock(RuleBasedSegmentStorageImpl.RuleBasedSegmentParser.class); - when(mParser.parse(any(), any())).thenAnswer(new Answer() { - @Override - public ParsedRuleBasedSegment answer(InvocationOnMock invocation) throws Throwable { - ParsedRuleBasedSegment mockResult = mock(ParsedRuleBasedSegment.class); - when(mockResult.getName()).thenReturn(((RuleBasedSegment) invocation.getArguments()[0]).getName()); - - return mockResult; - } - }); + mParser = new RuleBasedSegmentParser(new ParserCommons(mock(MySegmentsStorageContainer.class), mock(MySegmentsStorageContainer.class), mock(RuleBasedSegmentStorageProvider.class))); storage = new RuleBasedSegmentStorageImpl(mPersistentStorage, mParser); } diff --git a/src/test/java/io/split/android/client/utils/SplitClientImplFactory.java b/src/test/java/io/split/android/client/utils/SplitClientImplFactory.java index 36b8728a5..1c80f33c5 100644 --- a/src/test/java/io/split/android/client/utils/SplitClientImplFactory.java +++ b/src/test/java/io/split/android/client/utils/SplitClientImplFactory.java @@ -2,6 +2,8 @@ import static org.mockito.Mockito.mock; +import androidx.annotation.NonNull; + import java.util.Collections; import io.split.android.client.EventsTracker; @@ -17,7 +19,6 @@ import io.split.android.client.impressions.DecoratedImpressionListener; import io.split.android.client.impressions.ImpressionListener; import io.split.android.client.shared.SplitClientContainer; -import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; import io.split.android.client.storage.splits.SplitsStorage; import io.split.android.client.telemetry.storage.TelemetryStorage; import io.split.android.client.validators.KeyValidatorImpl; @@ -26,6 +27,7 @@ import io.split.android.client.validators.TreatmentManager; import io.split.android.client.validators.TreatmentManagerFactory; import io.split.android.client.validators.TreatmentManagerFactoryImpl; +import io.split.android.engine.experiments.ParserCommons; import io.split.android.engine.experiments.SplitParser; import io.split.android.fake.SplitTaskExecutorStub; @@ -35,7 +37,7 @@ public class SplitClientImplFactory { public static SplitClientImpl get(Key key, SplitsStorage splitsStorage) { SplitClientConfig cfg = SplitClientConfig.builder().build(); SplitEventsManager eventsManager = new SplitEventsManager(cfg, new SplitTaskExecutorStub()); - SplitParser splitParser = new SplitParser(mock(MySegmentsStorageContainer.class), mock(MySegmentsStorageContainer.class)); + SplitParser splitParser = getSplitParser(); TelemetryStorage telemetryStorage = mock(TelemetryStorage.class); TreatmentManagerFactory treatmentManagerFactory = new TreatmentManagerFactoryImpl( new KeyValidatorImpl(), new SplitValidatorImpl(), new ImpressionListener.FederatedImpressionListener(mock(DecoratedImpressionListener.class), Collections.emptyList()), @@ -62,7 +64,7 @@ false, new AttributesMergerImpl(), telemetryStorage, splitParser, } public static SplitClientImpl get(Key key, ImpressionListener impressionListener) { - SplitParser splitParser = new SplitParser(mock(MySegmentsStorageContainer.class), mock(MySegmentsStorageContainer.class)); + SplitParser splitParser = getSplitParser(); SplitClientConfig cfg = SplitClientConfig.builder().build(); return new SplitClientImpl( mock(SplitFactory.class), @@ -80,7 +82,7 @@ public static SplitClientImpl get(Key key, ImpressionListener impressionListener } public static SplitClientImpl get(Key key, SplitEventsManager eventsManager) { - SplitParser splitParser = new SplitParser(mock(MySegmentsStorageContainer.class), mock(MySegmentsStorageContainer.class)); + SplitParser splitParser = getSplitParser(); return new SplitClientImpl( mock(SplitFactory.class), mock(SplitClientContainer.class), @@ -95,4 +97,9 @@ public static SplitClientImpl get(Key key, SplitEventsManager eventsManager) { mock(TreatmentManager.class) ); } + + @NonNull + private static SplitParser getSplitParser() { + return new SplitParser(mock(ParserCommons.class)); + } } diff --git a/src/test/java/io/split/android/engine/experiments/EvaluatorTest.java b/src/test/java/io/split/android/engine/experiments/EvaluatorTest.java index 77d185093..a44f4e80d 100644 --- a/src/test/java/io/split/android/engine/experiments/EvaluatorTest.java +++ b/src/test/java/io/split/android/engine/experiments/EvaluatorTest.java @@ -22,9 +22,10 @@ import io.split.android.client.EvaluatorImpl; import io.split.android.client.TreatmentLabels; import io.split.android.client.dtos.Split; -import io.split.android.client.exceptions.ChangeNumberExceptionWrapper; import io.split.android.client.storage.mysegments.MySegmentsStorage; import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; import io.split.android.client.storage.splits.SplitsStorage; import io.split.android.grammar.Treatments; import io.split.android.helpers.FileHelper; @@ -41,12 +42,15 @@ public void loadSplitsFromFile() { MySegmentsStorage myLargeSegmentsStorage = mock(MySegmentsStorage.class); MySegmentsStorageContainer mySegmentsStorageContainer = mock(MySegmentsStorageContainer.class); MySegmentsStorageContainer myLargeSegmentsStorageContainer = mock(MySegmentsStorageContainer.class); + RuleBasedSegmentStorage ruleBasedSegmentStorage = mock(RuleBasedSegmentStorage.class); + RuleBasedSegmentStorageProvider ruleBasedSegmentStorageProvider = mock(RuleBasedSegmentStorageProvider.class); + when(ruleBasedSegmentStorageProvider.get()).thenReturn(ruleBasedSegmentStorage); SplitsStorage splitsStorage = mock(SplitsStorage.class); Set mySegments = new HashSet<>(Arrays.asList("s1", "s2", "test_copy")); Set myLargeSegments = new HashSet<>(Arrays.asList("segment1")); List splits = fileHelper.loadAndParseSplitChangeFile("split_changes_1.json"); - SplitParser splitParser = new SplitParser(mySegmentsStorageContainer, myLargeSegmentsStorageContainer); + SplitParser splitParser = new SplitParser(new ParserCommons(mySegmentsStorageContainer, myLargeSegmentsStorageContainer, ruleBasedSegmentStorageProvider)); Map splitsMap = splitsMap(splits); when(splitsStorage.getAll()).thenReturn(splitsMap); diff --git a/src/test/java/io/split/android/engine/experiments/RuleBasedSegmentParserTest.java b/src/test/java/io/split/android/engine/experiments/RuleBasedSegmentParserTest.java new file mode 100644 index 000000000..cfcefdc75 --- /dev/null +++ b/src/test/java/io/split/android/engine/experiments/RuleBasedSegmentParserTest.java @@ -0,0 +1,199 @@ +package io.split.android.engine.experiments; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import io.split.android.client.dtos.Condition; +import io.split.android.client.dtos.Excluded; +import io.split.android.client.dtos.RuleBasedSegment; +import io.split.android.client.dtos.Status; + +public class RuleBasedSegmentParserTest { + + private static final String SEGMENT_NAME = "test-segment"; + private static final String TRAFFIC_TYPE = "user"; + private static final long CHANGE_NUMBER = 123456789L; + private static final String MATCHING_KEY = "test-key"; + + private ParserCommons mParserCommons; + private RuleBasedSegmentParser mParser; + + @Before + public void setUp() { + mParserCommons = mock(ParserCommons.class); + mParser = new RuleBasedSegmentParser(mParserCommons); + } + + @Test + public void validParsing() { + List conditions = new ArrayList<>(); + List parsedConditions = new ArrayList<>(); + parsedConditions.add(mock(ParsedCondition.class)); + + Set excludedKeys = new HashSet<>(Arrays.asList("excluded1", "excluded2")); + Set excludedSegments = new HashSet<>(Arrays.asList("segment1", "segment2")); + + Excluded excluded = mock(Excluded.class); + when(excluded.getKeys()).thenReturn(excludedKeys); + when(excluded.getSegments()).thenReturn(excludedSegments); + + RuleBasedSegment segment = new RuleBasedSegment( + SEGMENT_NAME, + TRAFFIC_TYPE, + CHANGE_NUMBER, + Status.ACTIVE, + conditions, + excluded + ); + + when(mParserCommons.getParsedConditions( + eq(MATCHING_KEY), + eq(conditions), + anyString() + )).thenReturn(parsedConditions); + + ParsedRuleBasedSegment result = mParser.parse(segment, MATCHING_KEY); + + assertNotNull(result); + assertEquals(SEGMENT_NAME, result.getName()); + assertEquals(TRAFFIC_TYPE, result.getTrafficTypeName()); + assertEquals(CHANGE_NUMBER, result.getChangeNumber()); + assertEquals(excludedKeys, result.getExcludedKeys()); + assertEquals(excludedSegments, result.getExcludedSegments()); + assertEquals(parsedConditions, result.getParsedConditions()); + } + + @Test + public void parseWithNullConditionsCreatesEmptyConditionList() { + List conditions = new ArrayList<>(); + + Excluded excluded = mock(Excluded.class); + when(excluded.getKeys()).thenReturn(Collections.emptySet()); + when(excluded.getSegments()).thenReturn(Collections.emptySet()); + + RuleBasedSegment segment = new RuleBasedSegment( + SEGMENT_NAME, + TRAFFIC_TYPE, + CHANGE_NUMBER, + Status.ACTIVE, + conditions, + excluded + ); + + when(mParserCommons.getParsedConditions( + eq(MATCHING_KEY), + eq(conditions), + anyString() + )).thenReturn(null); + + ParsedRuleBasedSegment result = mParser.parse(segment, MATCHING_KEY); + + assertNotNull(result); + assertEquals(SEGMENT_NAME, result.getName()); + assertEquals(Collections.emptyList(), result.getParsedConditions()); + } + + @Test + public void parseWithNullMatchingKey() { + List conditions = new ArrayList<>(); + List parsedConditions = new ArrayList<>(); + parsedConditions.add(mock(ParsedCondition.class)); + + Excluded excluded = mock(Excluded.class); + when(excluded.getKeys()).thenReturn(Collections.emptySet()); + when(excluded.getSegments()).thenReturn(Collections.emptySet()); + + RuleBasedSegment segment = new RuleBasedSegment( + SEGMENT_NAME, + TRAFFIC_TYPE, + CHANGE_NUMBER, + Status.ACTIVE, + conditions, + excluded + ); + + when(mParserCommons.getParsedConditions( + eq(null), + eq(conditions), + anyString() + )).thenReturn(parsedConditions); + + ParsedRuleBasedSegment result = mParser.parse(segment, null); + + assertNotNull(result); + assertEquals(parsedConditions, result.getParsedConditions()); + } + + @Test + public void parseWithNullExcludedReturnsEmptyExcludedLists() { + List conditions = new ArrayList<>(); + List parsedConditions = new ArrayList<>(); + parsedConditions.add(mock(ParsedCondition.class)); + + RuleBasedSegment segment = new RuleBasedSegment( + SEGMENT_NAME, + TRAFFIC_TYPE, + CHANGE_NUMBER, + Status.ACTIVE, + conditions, + null + ); + + when(mParserCommons.getParsedConditions( + eq(MATCHING_KEY), + eq(conditions), + anyString() + )).thenReturn(parsedConditions); + + ParsedRuleBasedSegment result = mParser.parse(segment, MATCHING_KEY); + + assertNotNull(result); + assertTrue(result.getExcludedKeys().isEmpty()); + assertTrue(result.getExcludedSegments().isEmpty()); + } + + @Test + public void parseEmptyConditions() { + List conditions = Collections.emptyList(); + List parsedConditions = Collections.emptyList(); + + Excluded excluded = mock(Excluded.class); + when(excluded.getKeys()).thenReturn(Collections.emptySet()); + when(excluded.getSegments()).thenReturn(Collections.emptySet()); + + RuleBasedSegment segment = new RuleBasedSegment( + SEGMENT_NAME, + TRAFFIC_TYPE, + CHANGE_NUMBER, + Status.ACTIVE, + conditions, + excluded + ); + + when(mParserCommons.getParsedConditions( + eq(MATCHING_KEY), + eq(conditions), + anyString() + )).thenReturn(parsedConditions); + + ParsedRuleBasedSegment result = mParser.parse(segment, MATCHING_KEY); + + assertNotNull(result); + assertEquals(Collections.emptyList(), result.getParsedConditions()); + } +} diff --git a/src/test/java/io/split/android/engine/experiments/SplitParserTest.java b/src/test/java/io/split/android/engine/experiments/SplitParserTest.java index 6bdd68705..545d5c03f 100644 --- a/src/test/java/io/split/android/engine/experiments/SplitParserTest.java +++ b/src/test/java/io/split/android/engine/experiments/SplitParserTest.java @@ -4,6 +4,7 @@ import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.never; @@ -34,9 +35,12 @@ import io.split.android.client.dtos.Split; import io.split.android.client.dtos.Status; import io.split.android.client.dtos.UserDefinedLargeSegmentMatcherData; +import io.split.android.client.dtos.UserDefinedSegmentMatcherData; import io.split.android.client.dtos.WhitelistMatcherData; import io.split.android.client.storage.mysegments.MySegmentsStorage; import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; import io.split.android.engine.ConditionsTestUtil; import io.split.android.engine.matchers.AttributeMatcher; import io.split.android.engine.matchers.BetweenMatcher; @@ -65,11 +69,16 @@ public class SplitParserTest { MySegmentsStorageContainer mMySegmentsStorageContainer; @Mock MySegmentsStorageContainer mMyLargeSegmentsStorageContainer; + @Mock + RuleBasedSegmentStorage mRuleBasedSegmentStorage; + @Mock + RuleBasedSegmentStorageProvider mRuleBasedSegmentStorageProvider; @Before public void setup() { MockitoAnnotations.openMocks(this); when(mMySegmentsStorageContainer.getStorageForKey("")).thenReturn(mMySegmentsStorage); + when(mRuleBasedSegmentStorageProvider.get()).thenReturn(mRuleBasedSegmentStorage); } @Test @@ -155,11 +164,6 @@ public void equal_to_negative_number() { assertThat(actual, is(equalTo(expected))); } - @NonNull - private SplitParser createParser() { - return new SplitParser(mMySegmentsStorageContainer, mMyLargeSegmentsStorageContainer); - } - @Test public void between() { @@ -497,6 +501,33 @@ public void inLargeSegmentMatcherParsingTest() { verify(mMySegmentsStorageContainer, never()).getStorageForKey("matching_key"); } + @Test + public void inRuleBasedSegmentMatcherParsingTest() { + Condition condition = new Condition(); + condition.conditionType = ConditionType.ROLLOUT; + condition.label = null; + condition.partitions = null; + Matcher matcher = new Matcher(); + matcher.matcherType = MatcherType.IN_RULE_BASED_SEGMENT; + UserDefinedSegmentMatcherData userDefinedSegmentMatcherData = new UserDefinedSegmentMatcherData(); + userDefinedSegmentMatcherData.segmentName = "segment1"; + matcher.userDefinedSegmentMatcherData = userDefinedSegmentMatcherData; + condition.matcherGroup = new MatcherGroup(); + condition.matcherGroup.matchers = Collections.singletonList(matcher); + Split split = makeSplit("test1", Collections.singletonList(condition)); + + SplitParser parser = createParser(); + + ParsedSplit parsedSplit = parser.parse(split); + assertEquals("test1", parsedSplit.feature()); + assertEquals("off", parsedSplit.defaultTreatment()); + assertEquals(1, parsedSplit.parsedConditions().size()); + ParsedCondition parsedCondition = parsedSplit.parsedConditions().get(0); + assertNull(parsedCondition.label()); + assertEquals(ConditionType.ROLLOUT, parsedCondition.conditionType()); + assertNull(parsedCondition.partitions()); + } + @Test public void impressionsDisabledParsingTest(){ SplitParser parser = createParser(); @@ -513,6 +544,11 @@ public void impressionsDisabledParsingTest(){ assertTrue(actual2.impressionsDisabled()); } + @NonNull + private SplitParser createParser() { + return new SplitParser(new ParserCommons(mMySegmentsStorageContainer, mMyLargeSegmentsStorageContainer, mRuleBasedSegmentStorageProvider)); + } + private void set_matcher_test(Condition c, io.split.android.engine.matchers.Matcher m) { SplitParser parser = createParser(); diff --git a/src/test/java/io/split/android/engine/experiments/UnsupportedMatcherSplitParserTest.java b/src/test/java/io/split/android/engine/experiments/UnsupportedMatcherSplitParserTest.java index 4a972866d..a1f6f2e45 100644 --- a/src/test/java/io/split/android/engine/experiments/UnsupportedMatcherSplitParserTest.java +++ b/src/test/java/io/split/android/engine/experiments/UnsupportedMatcherSplitParserTest.java @@ -15,6 +15,8 @@ import io.split.android.client.dtos.Split; import io.split.android.client.storage.mysegments.MySegmentsStorage; import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; import io.split.android.client.utils.Json; public class UnsupportedMatcherSplitParserTest { @@ -27,6 +29,10 @@ public class UnsupportedMatcherSplitParserTest { private MySegmentsStorageContainer mMyLargeSegmentsStorageContainer; @Mock private DefaultConditionsProvider mDefaultConditionsProvider; + @Mock + private RuleBasedSegmentStorageProvider mRuleBasedSegmentStorageProvider; + @Mock + private RuleBasedSegmentStorage mRuleBasedSegmentStorage; private final Split mTestFlag = Json.fromJson("{\"changeNumber\":1709843458770,\"trafficTypeName\":\"user\",\"name\":\"feature_flag_for_test\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-1364119282,\"seed\":-605938843,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"algo\":2,\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"user\",\"attribute\":null},\"matcherType\":\"WRONG_MATCHER_TYPE\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"dependencyMatcherData\":null,\"booleanMatcherData\":null,\"stringMatcherData\":\"123123\"}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":100}],\"label\":\"wrong matcher type\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"user\",\"attribute\":\"sem\"},\"matcherType\":\"MATCHES_STRING\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"dependencyMatcherData\":null,\"booleanMatcherData\":null,\"stringMatcherData\":\"1.2.3\"}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0}],\"label\":\"sem matches 1.2.3\"}],\"configurations\":{},\"sets\":[]}", Split.class); private AutoCloseable mAutoCloseable; private SplitParser mSplitParser; @@ -37,7 +43,9 @@ public void setUp() { when(mMySegmentsStorageContainer.getStorageForKey("")).thenReturn(mMySegmentsStorage); when(mMySegmentsStorage.getAll()).thenReturn(Collections.emptySet()); when(mDefaultConditionsProvider.getDefaultConditions()).thenReturn(Collections.emptyList()); - mSplitParser = new SplitParser(mMySegmentsStorageContainer, mMyLargeSegmentsStorageContainer, mDefaultConditionsProvider); + when(mRuleBasedSegmentStorageProvider.get()).thenReturn(mRuleBasedSegmentStorage); + mSplitParser = new SplitParser( + new ParserCommons(mMySegmentsStorageContainer, mMyLargeSegmentsStorageContainer, mRuleBasedSegmentStorageProvider, mDefaultConditionsProvider)); } @After From 4ddb4aa1818e8489bea6b1f1d329fdeea7e2ed26 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Thu, 27 Feb 2025 18:08:17 -0300 Subject: [PATCH 18/26] Simplify initialization --- .../android/client/SplitFactoryHelper.java | 12 +++---- ...lhostRuleBasedSegmentsStorageProvider.java | 20 ----------- .../localhost/LocalhostSplitFactory.java | 7 ++-- .../storage/common/SplitStorageContainer.java | 8 ----- .../client/storage/db/StorageFactory.java | 6 ---- .../LazyRuleBasedSegmentStorageProvider.java | 25 -------------- .../rbs/RuleBasedSegmentStorageProvider.java | 9 ----- .../engine/experiments/ParserCommons.java | 33 ++++++++++++------- .../client/SplitClientImplBaseTest.java | 1 - .../android/client/SplitManagerImplTest.java | 1 - .../android/client/TreatmentManagerTest.java | 1 - ...zyRuleBasedSegmentStorageProviderTest.java | 29 ---------------- .../engine/experiments/EvaluatorTest.java | 1 - .../engine/experiments/SplitParserTest.java | 5 +-- .../UnsupportedMatcherSplitParserTest.java | 1 - 15 files changed, 33 insertions(+), 126 deletions(-) delete mode 100644 src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorageProvider.java delete mode 100644 src/main/java/io/split/android/client/storage/rbs/LazyRuleBasedSegmentStorageProvider.java delete mode 100644 src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageProvider.java delete mode 100644 src/test/java/io/split/android/client/storage/rbs/LazyRuleBasedSegmentStorageProviderTest.java diff --git a/src/main/java/io/split/android/client/SplitFactoryHelper.java b/src/main/java/io/split/android/client/SplitFactoryHelper.java index 09959aa64..06052bc25 100644 --- a/src/main/java/io/split/android/client/SplitFactoryHelper.java +++ b/src/main/java/io/split/android/client/SplitFactoryHelper.java @@ -86,10 +86,8 @@ import io.split.android.client.storage.events.PersistentEventsStorage; import io.split.android.client.storage.general.GeneralInfoStorage; import io.split.android.client.storage.impressions.PersistentImpressionsStorage; -import io.split.android.client.storage.rbs.LazyRuleBasedSegmentStorageProvider; import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; import io.split.android.client.storage.rbs.RuleBasedSegmentStorageImpl; -import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; import io.split.android.client.storage.splits.SplitsStorage; import io.split.android.client.telemetry.TelemetrySynchronizer; import io.split.android.client.telemetry.TelemetrySynchronizerImpl; @@ -193,7 +191,6 @@ SplitStorageContainer buildStorageContainer(UserConsent userConsentStatus, getTelemetryStorage(shouldRecordTelemetry, telemetryStorage), StorageFactory.getImpressionsObserverCachePersistentStorage(splitRoomDatabase, observerCacheExpirationPeriod, impressionsObserverExecutor), generalInfoStorage, - StorageFactory.getRuleBasedSegmentStorageProvider(), StorageFactory.getPersistentRuleBasedSegmentStorage(splitRoomDatabase, splitCipher, generalInfoStorage)); } @@ -474,16 +471,17 @@ ExecutorService getImpressionsLoggingTaskExecutor() { @NonNull static ParserCommons getParserCommons(SplitStorageContainer storageContainer) { - RuleBasedSegmentStorageProvider ruleBasedSegmentStorageProvider = storageContainer.getRuleBasedSegmentStorageProvider(); ParserCommons parserCommons = new ParserCommons( storageContainer.getMySegmentsStorageContainer(), - storageContainer.getMyLargeSegmentsStorageContainer(), - ruleBasedSegmentStorageProvider); + storageContainer.getMyLargeSegmentsStorageContainer()); + RuleBasedSegmentParser ruleBasedSegmentParser = new RuleBasedSegmentParser(parserCommons); RuleBasedSegmentStorage ruleBasedSegmentStorage = new RuleBasedSegmentStorageImpl(storageContainer.getPersistentRuleBasedSegmentStorage(), ruleBasedSegmentParser); - ((LazyRuleBasedSegmentStorageProvider) ruleBasedSegmentStorageProvider).set(ruleBasedSegmentStorage); + + parserCommons.setRuleBasedSegmentStorage(ruleBasedSegmentStorage); + return parserCommons; } diff --git a/src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorageProvider.java b/src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorageProvider.java deleted file mode 100644 index 9c4ae9e77..000000000 --- a/src/main/java/io/split/android/client/localhost/LocalhostRuleBasedSegmentsStorageProvider.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.split.android.client.localhost; - -import androidx.annotation.NonNull; - -import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; -import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; - -class LocalhostRuleBasedSegmentsStorageProvider implements RuleBasedSegmentStorageProvider { - - private final RuleBasedSegmentStorage mRuleBasedSegmentStorage; - - LocalhostRuleBasedSegmentsStorageProvider(@NonNull RuleBasedSegmentStorage ruleBasedSegmentStorage) { - mRuleBasedSegmentStorage = ruleBasedSegmentStorage; - } - - @Override - public RuleBasedSegmentStorage get() { - return mRuleBasedSegmentStorage; - } -} diff --git a/src/main/java/io/split/android/client/localhost/LocalhostSplitFactory.java b/src/main/java/io/split/android/client/localhost/LocalhostSplitFactory.java index 5f589f6d4..a472a5c00 100644 --- a/src/main/java/io/split/android/client/localhost/LocalhostSplitFactory.java +++ b/src/main/java/io/split/android/client/localhost/LocalhostSplitFactory.java @@ -107,10 +107,11 @@ public LocalhostSplitFactory(String key, Context context, @NonNull private static SplitParser getSplitParser() { - return new SplitParser(new ParserCommons( + ParserCommons parserCommons = new ParserCommons( new LocalhostMySegmentsStorageContainer(), - new LocalhostMySegmentsStorageContainer(), - new LocalhostRuleBasedSegmentsStorageProvider(new LocalhostRuleBasedSegmentsStorage()))); + new LocalhostMySegmentsStorageContainer()); + parserCommons.setRuleBasedSegmentStorage(new LocalhostRuleBasedSegmentsStorage()); + return new SplitParser(parserCommons); } @VisibleForTesting diff --git a/src/main/java/io/split/android/client/storage/common/SplitStorageContainer.java b/src/main/java/io/split/android/client/storage/common/SplitStorageContainer.java index 7205d8283..cff31a612 100644 --- a/src/main/java/io/split/android/client/storage/common/SplitStorageContainer.java +++ b/src/main/java/io/split/android/client/storage/common/SplitStorageContainer.java @@ -18,7 +18,6 @@ import io.split.android.client.storage.mysegments.MySegmentsStorage; import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; import io.split.android.client.storage.rbs.PersistentRuleBasedSegmentStorage; -import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; import io.split.android.client.storage.splits.PersistentSplitsStorage; import io.split.android.client.storage.splits.SplitsStorage; import io.split.android.client.telemetry.storage.TelemetryStorage; @@ -40,7 +39,6 @@ public class SplitStorageContainer { private final PersistentImpressionsUniqueStorage mPersistentImpressionsUniqueStorage; private final PersistentImpressionsObserverCacheStorage mPersistentImpressionsObserverCacheStorage; private final GeneralInfoStorage mGeneralInfoStorage; - private final RuleBasedSegmentStorageProvider mRuleBasedSegmentStorageProvider; private final PersistentRuleBasedSegmentStorage mPersistentRuleBasedSegmentStorage; public SplitStorageContainer(@NonNull SplitsStorage splitStorage, @@ -58,7 +56,6 @@ public SplitStorageContainer(@NonNull SplitsStorage splitStorage, @NonNull TelemetryStorage telemetryStorage, @NonNull PersistentImpressionsObserverCacheStorage persistentImpressionsObserverCacheStorage, @NonNull GeneralInfoStorage generalInfoStorage, - @NonNull RuleBasedSegmentStorageProvider ruleBasedSegmentStorageProvider, @NonNull PersistentRuleBasedSegmentStorage persistentRuleBasedSegmentStorage) { mSplitStorage = checkNotNull(splitStorage); @@ -76,7 +73,6 @@ public SplitStorageContainer(@NonNull SplitsStorage splitStorage, mPersistentImpressionsUniqueStorage = checkNotNull(persistentImpressionsUniqueStorage); mPersistentImpressionsObserverCacheStorage = checkNotNull(persistentImpressionsObserverCacheStorage); mGeneralInfoStorage = checkNotNull(generalInfoStorage); - mRuleBasedSegmentStorageProvider = checkNotNull(ruleBasedSegmentStorageProvider); mPersistentRuleBasedSegmentStorage = checkNotNull(persistentRuleBasedSegmentStorage); } @@ -152,10 +148,6 @@ public GeneralInfoStorage getGeneralInfoStorage() { return mGeneralInfoStorage; } - public RuleBasedSegmentStorageProvider getRuleBasedSegmentStorageProvider() { - return mRuleBasedSegmentStorageProvider; - } - public PersistentRuleBasedSegmentStorage getPersistentRuleBasedSegmentStorage() { return mPersistentRuleBasedSegmentStorage; } diff --git a/src/main/java/io/split/android/client/storage/db/StorageFactory.java b/src/main/java/io/split/android/client/storage/db/StorageFactory.java index 45b7b18bf..16dfee625 100644 --- a/src/main/java/io/split/android/client/storage/db/StorageFactory.java +++ b/src/main/java/io/split/android/client/storage/db/StorageFactory.java @@ -30,9 +30,7 @@ import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; import io.split.android.client.storage.mysegments.MySegmentsStorageContainerImpl; import io.split.android.client.storage.mysegments.SqLitePersistentMySegmentsStorage; -import io.split.android.client.storage.rbs.LazyRuleBasedSegmentStorageProvider; import io.split.android.client.storage.rbs.PersistentRuleBasedSegmentStorage; -import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; import io.split.android.client.storage.rbs.SqLitePersistentRuleBasedSegmentStorageProvider; import io.split.android.client.storage.splits.PersistentSplitsStorage; import io.split.android.client.storage.splits.SplitsStorage; @@ -162,8 +160,4 @@ public static GeneralInfoStorage getGeneralInfoStorage(SplitRoomDatabase splitRo public static PersistentRuleBasedSegmentStorage getPersistentRuleBasedSegmentStorage(SplitRoomDatabase splitRoomDatabase, SplitCipher splitCipher, GeneralInfoStorage generalInfoStorage) { return new SqLitePersistentRuleBasedSegmentStorageProvider(splitCipher, splitRoomDatabase, generalInfoStorage).get(); } - - public static RuleBasedSegmentStorageProvider getRuleBasedSegmentStorageProvider() { - return new LazyRuleBasedSegmentStorageProvider(); - } } diff --git a/src/main/java/io/split/android/client/storage/rbs/LazyRuleBasedSegmentStorageProvider.java b/src/main/java/io/split/android/client/storage/rbs/LazyRuleBasedSegmentStorageProvider.java deleted file mode 100644 index 9a68ebb2b..000000000 --- a/src/main/java/io/split/android/client/storage/rbs/LazyRuleBasedSegmentStorageProvider.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.split.android.client.storage.rbs; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.util.concurrent.atomic.AtomicReference; - -import io.split.android.client.utils.logger.Logger; - -public class LazyRuleBasedSegmentStorageProvider implements RuleBasedSegmentStorageProvider { - - private final AtomicReference mRuleBasedSegmentStorageRef = new AtomicReference<>(); - - public void set(@NonNull RuleBasedSegmentStorage ruleBasedSegmentStorage) { - if (!mRuleBasedSegmentStorageRef.compareAndSet(null, ruleBasedSegmentStorage)) { - Logger.w("RuleBasedSegmentStorage already set in LazyRuleBasedSegmentStorageProvider"); - } - } - - @Nullable - @Override - public RuleBasedSegmentStorage get() { - return mRuleBasedSegmentStorageRef.get(); - } -} diff --git a/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageProvider.java b/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageProvider.java deleted file mode 100644 index 4c4393a24..000000000 --- a/src/main/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageProvider.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.split.android.client.storage.rbs; - -import androidx.annotation.Nullable; - -public interface RuleBasedSegmentStorageProvider { - - @Nullable - RuleBasedSegmentStorage get(); -} diff --git a/src/main/java/io/split/android/engine/experiments/ParserCommons.java b/src/main/java/io/split/android/engine/experiments/ParserCommons.java index 168a9b898..8bfdcd6e8 100644 --- a/src/main/java/io/split/android/engine/experiments/ParserCommons.java +++ b/src/main/java/io/split/android/engine/experiments/ParserCommons.java @@ -16,7 +16,7 @@ import io.split.android.client.dtos.Partition; import io.split.android.client.storage.mysegments.EmptyMySegmentsStorage; import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; -import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; +import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; import io.split.android.client.utils.logger.Logger; import io.split.android.engine.matchers.AllKeysMatcher; import io.split.android.engine.matchers.AttributeMatcher; @@ -49,27 +49,28 @@ public class ParserCommons { public static final int CONDITIONS_UPPER_LIMIT = 50; private final MySegmentsStorageContainer mMySegmentsStorageContainer; private final MySegmentsStorageContainer mMyLargeSegmentsStorageContainer; - private final RuleBasedSegmentStorageProvider mRuleBasedSegmentStorageProvider; + private RuleBasedSegmentStorage mRuleBasedSegmentStorage; private final DefaultConditionsProvider mDefaultConditionsProvider; private EmptyMySegmentsStorage mEmptyMySegmentsStorage; public ParserCommons(@NonNull MySegmentsStorageContainer mySegmentsStorageContainer, - @NonNull MySegmentsStorageContainer myLargeSegmentsStorageContainer, - @NonNull RuleBasedSegmentStorageProvider ruleBasedSegmentStorageProvider) { - this(mySegmentsStorageContainer, myLargeSegmentsStorageContainer, ruleBasedSegmentStorageProvider, new DefaultConditionsProvider()); + @NonNull MySegmentsStorageContainer myLargeSegmentsStorageContainer) { + this(mySegmentsStorageContainer, myLargeSegmentsStorageContainer, new DefaultConditionsProvider()); } @VisibleForTesting ParserCommons(@NonNull MySegmentsStorageContainer mySegmentsStorageContainer, @NonNull MySegmentsStorageContainer myLargeSegmentsStorageContainer, - @NonNull RuleBasedSegmentStorageProvider ruleBasedSegmentStorageProvider, DefaultConditionsProvider defaultConditionsProvider) { mMySegmentsStorageContainer = checkNotNull(mySegmentsStorageContainer); mMyLargeSegmentsStorageContainer = checkNotNull(myLargeSegmentsStorageContainer); - mRuleBasedSegmentStorageProvider = checkNotNull(ruleBasedSegmentStorageProvider); mDefaultConditionsProvider = checkNotNull(defaultConditionsProvider); } + public void setRuleBasedSegmentStorage(@NonNull RuleBasedSegmentStorage ruleBasedSegmentStorage) { + mRuleBasedSegmentStorage = checkNotNull(ruleBasedSegmentStorage); + } + @Nullable List getParsedConditions(String matchingKey, List conditions, String largeConditionSizeMessage) { if (conditions.size() > CONDITIONS_UPPER_LIMIT) { @@ -108,7 +109,7 @@ private CombiningMatcher toMatcher(MatcherGroup matcherGroup, String matchingKey } private AttributeMatcher toMatcher(Matcher matcher, String matchingKey) throws UnsupportedMatcherException { - io.split.android.engine.matchers.Matcher delegate; + io.split.android.engine.matchers.Matcher delegate = null; // Values not present in {@link io.split.android.client.dtos.MatcherType} are deserialized as null if (matcher.matcherType == null) { @@ -209,9 +210,14 @@ private AttributeMatcher toMatcher(Matcher matcher, String matchingKey) throws U delegate = new InListSemverMatcher(matcher.whitelistMatcherData.whitelist); break; case IN_RULE_BASED_SEGMENT: - delegate = new InRuleBasedSegmentMatcher(mRuleBasedSegmentStorageProvider.get(), - (matchingKey != null) ? mMySegmentsStorageContainer.getStorageForKey(matchingKey) : getEmptyMySegmentsStorage(), - matcher.userDefinedSegmentMatcherData.segmentName); + if (mRuleBasedSegmentStorage != null) { + delegate = new InRuleBasedSegmentMatcher(mRuleBasedSegmentStorage, + (matchingKey != null) ? mMySegmentsStorageContainer.getStorageForKey(matchingKey) : getEmptyMySegmentsStorage(), + matcher.userDefinedSegmentMatcherData.segmentName); + } else { + // shouldn't happen + Logger.w("RuleBasedSegmentStorage not set in ParserCommons"); + } break; default: // since values not present in {@link io.split.android.client.dtos.MatcherType} @@ -219,6 +225,10 @@ private AttributeMatcher toMatcher(Matcher matcher, String matchingKey) throws U throw new UnsupportedMatcherException("Unable to create matcher for matcher type: " + matcher.matcherType); } + if (delegate == null) { + throw new UnsupportedMatcherException("Unable to create matcher for matcher type: " + matcher.matcherType); + } + String attribute = null; if (matcher.keySelector != null && matcher.keySelector.attribute != null) { attribute = matcher.keySelector.attribute; @@ -226,7 +236,6 @@ private AttributeMatcher toMatcher(Matcher matcher, String matchingKey) throws U boolean negate = matcher.negate; - return new AttributeMatcher(attribute, delegate, negate); } diff --git a/src/test/java/io/split/android/client/SplitClientImplBaseTest.java b/src/test/java/io/split/android/client/SplitClientImplBaseTest.java index e0b8fc56f..c9ed77def 100644 --- a/src/test/java/io/split/android/client/SplitClientImplBaseTest.java +++ b/src/test/java/io/split/android/client/SplitClientImplBaseTest.java @@ -15,7 +15,6 @@ import io.split.android.client.storage.mysegments.MySegmentsStorage; import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; -import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; import io.split.android.client.storage.splits.SplitsStorage; import io.split.android.client.validators.SplitValidator; import io.split.android.client.validators.TreatmentManager; diff --git a/src/test/java/io/split/android/client/SplitManagerImplTest.java b/src/test/java/io/split/android/client/SplitManagerImplTest.java index 752ec1feb..a086c6045 100644 --- a/src/test/java/io/split/android/client/SplitManagerImplTest.java +++ b/src/test/java/io/split/android/client/SplitManagerImplTest.java @@ -28,7 +28,6 @@ import io.split.android.client.storage.mysegments.MySegmentsStorage; import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; -import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; import io.split.android.client.storage.splits.SplitsStorage; import io.split.android.client.validators.SplitValidator; import io.split.android.client.validators.SplitValidatorImpl; diff --git a/src/test/java/io/split/android/client/TreatmentManagerTest.java b/src/test/java/io/split/android/client/TreatmentManagerTest.java index 13b8fe6aa..81eebd4e9 100644 --- a/src/test/java/io/split/android/client/TreatmentManagerTest.java +++ b/src/test/java/io/split/android/client/TreatmentManagerTest.java @@ -33,7 +33,6 @@ import io.split.android.client.storage.mysegments.MySegmentsStorage; import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; -import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; import io.split.android.client.storage.splits.SplitsStorage; import io.split.android.client.telemetry.storage.TelemetryStorageProducer; import io.split.android.client.utils.Utils; diff --git a/src/test/java/io/split/android/client/storage/rbs/LazyRuleBasedSegmentStorageProviderTest.java b/src/test/java/io/split/android/client/storage/rbs/LazyRuleBasedSegmentStorageProviderTest.java deleted file mode 100644 index 01b84b6a7..000000000 --- a/src/test/java/io/split/android/client/storage/rbs/LazyRuleBasedSegmentStorageProviderTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.split.android.client.storage.rbs; - -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.mockito.Mockito.mock; - -import org.junit.Test; - -public class LazyRuleBasedSegmentStorageProviderTest { - - @Test - public void refCanOnlyBeSetOnce() { - LazyRuleBasedSegmentStorageProvider provider = new LazyRuleBasedSegmentStorageProvider(); - RuleBasedSegmentStorage firstInstance = mock(RuleBasedSegmentStorage.class); - RuleBasedSegmentStorage secondInstance = mock(RuleBasedSegmentStorage.class); - provider.set(firstInstance); - provider.set(secondInstance); - - assertSame(firstInstance, provider.get()); - assertNotSame(secondInstance, provider.get()); - } - - @Test - public void getReturnsNullWhenSetHasNotBeenCalled() { - LazyRuleBasedSegmentStorageProvider provider = new LazyRuleBasedSegmentStorageProvider(); - assertNull(provider.get()); - } -} diff --git a/src/test/java/io/split/android/engine/experiments/EvaluatorTest.java b/src/test/java/io/split/android/engine/experiments/EvaluatorTest.java index a44f4e80d..ce2734ca8 100644 --- a/src/test/java/io/split/android/engine/experiments/EvaluatorTest.java +++ b/src/test/java/io/split/android/engine/experiments/EvaluatorTest.java @@ -25,7 +25,6 @@ import io.split.android.client.storage.mysegments.MySegmentsStorage; import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; -import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; import io.split.android.client.storage.splits.SplitsStorage; import io.split.android.grammar.Treatments; import io.split.android.helpers.FileHelper; diff --git a/src/test/java/io/split/android/engine/experiments/SplitParserTest.java b/src/test/java/io/split/android/engine/experiments/SplitParserTest.java index 545d5c03f..7d61cc3f4 100644 --- a/src/test/java/io/split/android/engine/experiments/SplitParserTest.java +++ b/src/test/java/io/split/android/engine/experiments/SplitParserTest.java @@ -40,7 +40,6 @@ import io.split.android.client.storage.mysegments.MySegmentsStorage; import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; -import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; import io.split.android.engine.ConditionsTestUtil; import io.split.android.engine.matchers.AttributeMatcher; import io.split.android.engine.matchers.BetweenMatcher; @@ -546,7 +545,9 @@ public void impressionsDisabledParsingTest(){ @NonNull private SplitParser createParser() { - return new SplitParser(new ParserCommons(mMySegmentsStorageContainer, mMyLargeSegmentsStorageContainer, mRuleBasedSegmentStorageProvider)); + ParserCommons parserCommons = new ParserCommons(mMySegmentsStorageContainer, mMyLargeSegmentsStorageContainer); + parserCommons.setRuleBasedSegmentStorage(mRuleBasedSegmentStorage); + return new SplitParser(parserCommons); } private void set_matcher_test(Condition c, io.split.android.engine.matchers.Matcher m) { diff --git a/src/test/java/io/split/android/engine/experiments/UnsupportedMatcherSplitParserTest.java b/src/test/java/io/split/android/engine/experiments/UnsupportedMatcherSplitParserTest.java index a1f6f2e45..8ced0e858 100644 --- a/src/test/java/io/split/android/engine/experiments/UnsupportedMatcherSplitParserTest.java +++ b/src/test/java/io/split/android/engine/experiments/UnsupportedMatcherSplitParserTest.java @@ -16,7 +16,6 @@ import io.split.android.client.storage.mysegments.MySegmentsStorage; import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; import io.split.android.client.storage.rbs.RuleBasedSegmentStorage; -import io.split.android.client.storage.rbs.RuleBasedSegmentStorageProvider; import io.split.android.client.utils.Json; public class UnsupportedMatcherSplitParserTest { From 509204ea2153f80396b198279bd5839408422025 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Thu, 27 Feb 2025 18:16:01 -0300 Subject: [PATCH 19/26] Fix tests --- .../io/split/android/client/SplitClientImplBaseTest.java | 7 +------ .../java/io/split/android/client/SplitManagerImplTest.java | 5 +---- .../java/io/split/android/client/TreatmentManagerTest.java | 4 +--- .../storage/rbs/RuleBasedSegmentStorageImplTest.java | 2 +- .../io/split/android/engine/experiments/EvaluatorTest.java | 4 +--- .../split/android/engine/experiments/SplitParserTest.java | 3 --- .../experiments/UnsupportedMatcherSplitParserTest.java | 5 +---- 7 files changed, 6 insertions(+), 24 deletions(-) diff --git a/src/test/java/io/split/android/client/SplitClientImplBaseTest.java b/src/test/java/io/split/android/client/SplitClientImplBaseTest.java index c9ed77def..e1f7f6811 100644 --- a/src/test/java/io/split/android/client/SplitClientImplBaseTest.java +++ b/src/test/java/io/split/android/client/SplitClientImplBaseTest.java @@ -1,7 +1,5 @@ package io.split.android.client; -import static org.mockito.Mockito.when; - import org.junit.Before; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -37,8 +35,6 @@ public abstract class SplitClientImplBaseTest { @Mock protected RuleBasedSegmentStorage ruleBasedSegmentStorage; @Mock - protected RuleBasedSegmentStorageProvider ruleBasedSegmentStorageProvider; - @Mock protected MySegmentsStorage mySegmentsStorage; @Mock protected ImpressionListener impressionListener; @@ -60,13 +56,12 @@ public void setUp() { MockitoAnnotations.openMocks(this); SplitClientConfig splitClientConfig = SplitClientConfig.builder().build(); - when(ruleBasedSegmentStorageProvider.get()).thenReturn(ruleBasedSegmentStorage); splitClient = new SplitClientImpl( container, clientContainer, new Key("test_key"), - new SplitParser(new ParserCommons(mySegmentsStorageContainer, myLargeSegmentsStorageContainer, ruleBasedSegmentStorageProvider)), + new SplitParser(new ParserCommons(mySegmentsStorageContainer, myLargeSegmentsStorageContainer)), impressionListener, splitClientConfig, new SplitEventsManager(splitClientConfig, new SplitTaskExecutorStub()), diff --git a/src/test/java/io/split/android/client/SplitManagerImplTest.java b/src/test/java/io/split/android/client/SplitManagerImplTest.java index a086c6045..180bbf3ff 100644 --- a/src/test/java/io/split/android/client/SplitManagerImplTest.java +++ b/src/test/java/io/split/android/client/SplitManagerImplTest.java @@ -52,8 +52,6 @@ public class SplitManagerImplTest { @Mock RuleBasedSegmentStorage mRuleBasedSegmentStorage; @Mock - RuleBasedSegmentStorageProvider mRuleBasedSegmentStorageProvider; - @Mock SplitManager mSplitManager; @Before @@ -61,8 +59,7 @@ public void setup() { MockitoAnnotations.openMocks(this); SplitValidator validator = new SplitValidatorImpl(); when(mMySegmentsStorageContainer.getStorageForKey("")).thenReturn(mMySegmentsStorage); - when(mRuleBasedSegmentStorageProvider.get()).thenReturn(mRuleBasedSegmentStorage); - SplitParser parser = new SplitParser(new ParserCommons(mMySegmentsStorageContainer, mMyLargeSegmentsStorageContainer, mRuleBasedSegmentStorageProvider)); + SplitParser parser = new SplitParser(new ParserCommons(mMySegmentsStorageContainer, mMyLargeSegmentsStorageContainer)); mSplitManager = new SplitManagerImpl(mSplitsStorage, validator, parser); } diff --git a/src/test/java/io/split/android/client/TreatmentManagerTest.java b/src/test/java/io/split/android/client/TreatmentManagerTest.java index 81eebd4e9..d5c7ec792 100644 --- a/src/test/java/io/split/android/client/TreatmentManagerTest.java +++ b/src/test/java/io/split/android/client/TreatmentManagerTest.java @@ -76,13 +76,11 @@ public void loadSplitsFromFile() { MySegmentsStorageContainer myLargeSegmentsStorageContainer = mock(MySegmentsStorageContainer.class); MySegmentsStorage mySegmentsStorage = mock(MySegmentsStorage.class); RuleBasedSegmentStorage ruleBasedSegmentStorage = mock(RuleBasedSegmentStorage.class); - RuleBasedSegmentStorageProvider ruleBasedSegmentStorageProvider = mock(RuleBasedSegmentStorageProvider.class); - when(ruleBasedSegmentStorageProvider.get()).thenReturn(ruleBasedSegmentStorage); SplitsStorage splitsStorage = mock(SplitsStorage.class); Set mySegments = new HashSet(Arrays.asList("s1", "s2", "test_copy")); List splits = fileHelper.loadAndParseSplitChangeFile("split_changes_1.json"); - SplitParser splitParser = new SplitParser(new ParserCommons(mySegmentsStorageContainer, myLargeSegmentsStorageContainer, ruleBasedSegmentStorageProvider)); + SplitParser splitParser = new SplitParser(new ParserCommons(mySegmentsStorageContainer, myLargeSegmentsStorageContainer)); Map splitsMap = splitsMap(splits); when(splitsStorage.getAll()).thenReturn(splitsMap); diff --git a/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java b/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java index 111c93e05..5ed299a65 100644 --- a/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java +++ b/src/test/java/io/split/android/client/storage/rbs/RuleBasedSegmentStorageImplTest.java @@ -36,7 +36,7 @@ public class RuleBasedSegmentStorageImplTest { @Before public void setUp() { mPersistentStorage = mock(PersistentRuleBasedSegmentStorage.class); - mParser = new RuleBasedSegmentParser(new ParserCommons(mock(MySegmentsStorageContainer.class), mock(MySegmentsStorageContainer.class), mock(RuleBasedSegmentStorageProvider.class))); + mParser = new RuleBasedSegmentParser(new ParserCommons(mock(MySegmentsStorageContainer.class), mock(MySegmentsStorageContainer.class))); storage = new RuleBasedSegmentStorageImpl(mPersistentStorage, mParser); } diff --git a/src/test/java/io/split/android/engine/experiments/EvaluatorTest.java b/src/test/java/io/split/android/engine/experiments/EvaluatorTest.java index ce2734ca8..f75e1e8d6 100644 --- a/src/test/java/io/split/android/engine/experiments/EvaluatorTest.java +++ b/src/test/java/io/split/android/engine/experiments/EvaluatorTest.java @@ -42,14 +42,12 @@ public void loadSplitsFromFile() { MySegmentsStorageContainer mySegmentsStorageContainer = mock(MySegmentsStorageContainer.class); MySegmentsStorageContainer myLargeSegmentsStorageContainer = mock(MySegmentsStorageContainer.class); RuleBasedSegmentStorage ruleBasedSegmentStorage = mock(RuleBasedSegmentStorage.class); - RuleBasedSegmentStorageProvider ruleBasedSegmentStorageProvider = mock(RuleBasedSegmentStorageProvider.class); - when(ruleBasedSegmentStorageProvider.get()).thenReturn(ruleBasedSegmentStorage); SplitsStorage splitsStorage = mock(SplitsStorage.class); Set mySegments = new HashSet<>(Arrays.asList("s1", "s2", "test_copy")); Set myLargeSegments = new HashSet<>(Arrays.asList("segment1")); List splits = fileHelper.loadAndParseSplitChangeFile("split_changes_1.json"); - SplitParser splitParser = new SplitParser(new ParserCommons(mySegmentsStorageContainer, myLargeSegmentsStorageContainer, ruleBasedSegmentStorageProvider)); + SplitParser splitParser = new SplitParser(new ParserCommons(mySegmentsStorageContainer, myLargeSegmentsStorageContainer)); Map splitsMap = splitsMap(splits); when(splitsStorage.getAll()).thenReturn(splitsMap); diff --git a/src/test/java/io/split/android/engine/experiments/SplitParserTest.java b/src/test/java/io/split/android/engine/experiments/SplitParserTest.java index 7d61cc3f4..9a48de3ad 100644 --- a/src/test/java/io/split/android/engine/experiments/SplitParserTest.java +++ b/src/test/java/io/split/android/engine/experiments/SplitParserTest.java @@ -70,14 +70,11 @@ public class SplitParserTest { MySegmentsStorageContainer mMyLargeSegmentsStorageContainer; @Mock RuleBasedSegmentStorage mRuleBasedSegmentStorage; - @Mock - RuleBasedSegmentStorageProvider mRuleBasedSegmentStorageProvider; @Before public void setup() { MockitoAnnotations.openMocks(this); when(mMySegmentsStorageContainer.getStorageForKey("")).thenReturn(mMySegmentsStorage); - when(mRuleBasedSegmentStorageProvider.get()).thenReturn(mRuleBasedSegmentStorage); } @Test diff --git a/src/test/java/io/split/android/engine/experiments/UnsupportedMatcherSplitParserTest.java b/src/test/java/io/split/android/engine/experiments/UnsupportedMatcherSplitParserTest.java index 8ced0e858..2948b7978 100644 --- a/src/test/java/io/split/android/engine/experiments/UnsupportedMatcherSplitParserTest.java +++ b/src/test/java/io/split/android/engine/experiments/UnsupportedMatcherSplitParserTest.java @@ -29,8 +29,6 @@ public class UnsupportedMatcherSplitParserTest { @Mock private DefaultConditionsProvider mDefaultConditionsProvider; @Mock - private RuleBasedSegmentStorageProvider mRuleBasedSegmentStorageProvider; - @Mock private RuleBasedSegmentStorage mRuleBasedSegmentStorage; private final Split mTestFlag = Json.fromJson("{\"changeNumber\":1709843458770,\"trafficTypeName\":\"user\",\"name\":\"feature_flag_for_test\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-1364119282,\"seed\":-605938843,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"algo\":2,\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"user\",\"attribute\":null},\"matcherType\":\"WRONG_MATCHER_TYPE\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"dependencyMatcherData\":null,\"booleanMatcherData\":null,\"stringMatcherData\":\"123123\"}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":100}],\"label\":\"wrong matcher type\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"user\",\"attribute\":\"sem\"},\"matcherType\":\"MATCHES_STRING\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"dependencyMatcherData\":null,\"booleanMatcherData\":null,\"stringMatcherData\":\"1.2.3\"}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0}],\"label\":\"sem matches 1.2.3\"}],\"configurations\":{},\"sets\":[]}", Split.class); private AutoCloseable mAutoCloseable; @@ -42,9 +40,8 @@ public void setUp() { when(mMySegmentsStorageContainer.getStorageForKey("")).thenReturn(mMySegmentsStorage); when(mMySegmentsStorage.getAll()).thenReturn(Collections.emptySet()); when(mDefaultConditionsProvider.getDefaultConditions()).thenReturn(Collections.emptyList()); - when(mRuleBasedSegmentStorageProvider.get()).thenReturn(mRuleBasedSegmentStorage); mSplitParser = new SplitParser( - new ParserCommons(mMySegmentsStorageContainer, mMyLargeSegmentsStorageContainer, mRuleBasedSegmentStorageProvider, mDefaultConditionsProvider)); + new ParserCommons(mMySegmentsStorageContainer, mMyLargeSegmentsStorageContainer, mDefaultConditionsProvider)); } @After From 2672fbf133284c9489f278e9fe65ecfd79d89586 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Fri, 28 Feb 2025 17:46:01 -0300 Subject: [PATCH 20/26] WIP --- .../java/helper/IntegrationHelper.java | 5 +- .../observer/DedupeIntegrationTest.java | 9 +-- .../integration/FlagsSpecInRequestTest.java | 9 +-- .../integration/InitialChangeNumberTest.java | 2 +- .../tests/integration/IntegrationTest.java | 2 +- .../tests/integration/SingleSyncTest.java | 6 +- .../tests/integration/SplitChangesTest.java | 2 +- .../encryption/EncryptionTest.java | 3 +- .../integration/init/InitializationTest.java | 6 +- .../matcher/UnsupportedMatcherTest.java | 6 +- .../integration/streaming/ControlTest.java | 3 +- .../telemetry/TelemetryIntegrationTest.java | 8 +-- .../toggle/ImpressionsToggleTest.java | 6 +- .../client/dtos/RuleBasedSegmentChange.java | 29 ++++++++ .../client/dtos/TargetingRulesChange.java | 29 ++++++++ .../client/service/ServiceFactory.java | 8 +-- .../client/service/SplitApiFacade.java | 8 +-- .../rules/TargetingRulesResponseParser.java | 22 +++++++ .../service/splits/SplitsSyncHelper.java | 11 ++-- .../workmanager/splits/FetcherProvider.java | 4 +- .../splits/SyncHelperProvider.java | 10 +-- .../engine/experiments/ParserCommons.java | 3 +- .../client/service/HttpFetcherTest.java | 64 +++++++++++------- .../client/service/SplitsSyncHelperTest.java | 66 ++++++++++--------- .../client/service/SynchronizerTest.java | 4 +- .../TargetingRulesResponseParserTest.java | 5 ++ .../SplitsSyncWorkerTaskBuilderTest.java | 4 +- 27 files changed, 208 insertions(+), 126 deletions(-) create mode 100644 src/main/java/io/split/android/client/dtos/RuleBasedSegmentChange.java create mode 100644 src/main/java/io/split/android/client/dtos/TargetingRulesChange.java create mode 100644 src/main/java/io/split/android/client/service/rules/TargetingRulesResponseParser.java create mode 100644 src/test/java/io/split/android/client/service/rules/TargetingRulesResponseParserTest.java diff --git a/src/androidTest/java/helper/IntegrationHelper.java b/src/androidTest/java/helper/IntegrationHelper.java index 6144c6a7b..a8c0182c1 100644 --- a/src/androidTest/java/helper/IntegrationHelper.java +++ b/src/androidTest/java/helper/IntegrationHelper.java @@ -40,6 +40,7 @@ import io.split.android.client.api.Key; import io.split.android.client.dtos.Event; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.dtos.TestImpressions; import io.split.android.client.lifecycle.SplitLifecycleManager; import io.split.android.client.network.HttpClient; @@ -99,7 +100,7 @@ public static String emptySplitChanges(long since, long till) { } public static String emptySplitChanges(long till) { - return String.format("{\"splits\":[], \"since\": %d, \"till\": %d }", till, till); + return String.format("{\"ff\":{\"splits\":[], \"since\": %d, \"till\": %d},\"rbs\":{\"d\":[],\"s\":%d,\"t\":%d}}", till, till, till, till); } public static SplitFactory buildFactory(String apiToken, Key key, SplitClientConfig config, @@ -319,7 +320,7 @@ public static String loadSplitChanges(Context context, String fileName) { String change = fileHelper.loadFileContent(context, fileName); SplitChange parsedChange = Json.fromJson(change, SplitChange.class); parsedChange.since = parsedChange.till; - return Json.toJson(parsedChange); + return Json.toJson(TargetingRulesChange.create(parsedChange)); } /** diff --git a/src/androidTest/java/io/split/android/client/service/impressions/observer/DedupeIntegrationTest.java b/src/androidTest/java/io/split/android/client/service/impressions/observer/DedupeIntegrationTest.java index d3f9152f4..7b14d8020 100644 --- a/src/androidTest/java/io/split/android/client/service/impressions/observer/DedupeIntegrationTest.java +++ b/src/androidTest/java/io/split/android/client/service/impressions/observer/DedupeIntegrationTest.java @@ -3,7 +3,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; - import static helper.IntegrationHelper.getSinceFromUri; import android.content.Context; @@ -29,13 +28,11 @@ import fake.LifecycleManagerStub; import fake.SynchronizerSpyImpl; import helper.DatabaseHelper; -import helper.FileHelper; import helper.IntegrationHelper; import helper.TestableSplitConfigBuilder; import io.split.android.client.SplitClient; import io.split.android.client.SplitFactory; import io.split.android.client.dtos.KeyImpression; -import io.split.android.client.dtos.SplitChange; import io.split.android.client.events.SplitEvent; import io.split.android.client.impressions.Impression; import io.split.android.client.impressions.ImpressionListener; @@ -341,10 +338,6 @@ private SplitFactory initSplitFactory(TestableSplitConfigBuilder builder, HttpCl } private String loadSplitChanges() { - FileHelper fileHelper = new FileHelper(); - String change = fileHelper.loadFileContent(mContext, "split_changes_1.json"); - SplitChange parsedChange = Json.fromJson(change, SplitChange.class); - parsedChange.since = parsedChange.till; - return Json.toJson(parsedChange); + return IntegrationHelper.loadSplitChanges(mContext, "split_changes_1.json"); } } diff --git a/src/androidTest/java/tests/integration/FlagsSpecInRequestTest.java b/src/androidTest/java/tests/integration/FlagsSpecInRequestTest.java index c289ef64e..040d732d8 100644 --- a/src/androidTest/java/tests/integration/FlagsSpecInRequestTest.java +++ b/src/androidTest/java/tests/integration/FlagsSpecInRequestTest.java @@ -26,18 +26,15 @@ import fake.HttpResponseMock; import fake.HttpResponseMockDispatcher; import helper.DatabaseHelper; -import helper.FileHelper; import helper.IntegrationHelper; import helper.TestableSplitConfigBuilder; import io.split.android.client.SplitClient; import io.split.android.client.SplitFactory; import io.split.android.client.TestingConfig; -import io.split.android.client.dtos.SplitChange; import io.split.android.client.events.SplitEvent; import io.split.android.client.storage.db.GeneralInfoEntity; import io.split.android.client.storage.db.SplitEntity; import io.split.android.client.storage.db.SplitRoomDatabase; -import io.split.android.client.utils.Json; import tests.integration.shared.TestingHelper; public class FlagsSpecInRequestTest { @@ -197,11 +194,7 @@ private SplitFactory initSplitFactory(TestableSplitConfigBuilder builder, HttpCl } private String loadSplitChanges() { - FileHelper fileHelper = new FileHelper(); - String change = fileHelper.loadFileContent(mContext, "split_changes_1.json"); - SplitChange parsedChange = Json.fromJson(change, SplitChange.class); - parsedChange.since = parsedChange.till; - return Json.toJson(parsedChange); + return IntegrationHelper.loadSplitChanges(mContext, "split_changes_1.json"); } private static SplitEntity newSplitEntity(String name, String trafficType, Set sets) { diff --git a/src/androidTest/java/tests/integration/InitialChangeNumberTest.java b/src/androidTest/java/tests/integration/InitialChangeNumberTest.java index a8b64a13e..95de23a06 100644 --- a/src/androidTest/java/tests/integration/InitialChangeNumberTest.java +++ b/src/androidTest/java/tests/integration/InitialChangeNumberTest.java @@ -74,7 +74,7 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio mIsFirstChangeNumber = false; } return new MockResponse().setResponseCode(200) - .setBody("{\"splits\":[], \"since\":" + changeNumber + ", \"till\":" + (changeNumber) + "}"); + .setBody(IntegrationHelper.emptySplitChanges(changeNumber)); } else if (request.getPath().contains("/events/bulk")) { String trackRequestBody = request.getBody().readUtf8(); diff --git a/src/androidTest/java/tests/integration/IntegrationTest.java b/src/androidTest/java/tests/integration/IntegrationTest.java index 07f8d8c37..2b872204f 100644 --- a/src/androidTest/java/tests/integration/IntegrationTest.java +++ b/src/androidTest/java/tests/integration/IntegrationTest.java @@ -410,7 +410,7 @@ private String splitsPerRequest(int reqId) { if (reqId < req) { req = reqId; } - return mJsonChanges.get(req); + return "{\"ff\":" + mJsonChanges.get(req) + "\"}"; } private void loadSplitChanges() { diff --git a/src/androidTest/java/tests/integration/SingleSyncTest.java b/src/androidTest/java/tests/integration/SingleSyncTest.java index 4dd623b3b..13f540006 100644 --- a/src/androidTest/java/tests/integration/SingleSyncTest.java +++ b/src/androidTest/java/tests/integration/SingleSyncTest.java @@ -332,10 +332,6 @@ private HttpStreamResponseMock createStreamResponse(int status, BlockingQueue s.name.equals("FACUNDO_TEST") || s.name.equals("testing")).collect(Collectors.toList()); parsedChange.since = parsedChange.till; - return Json.toJson(parsedChange); + return Json.toJson(TargetingRulesChange.create(parsedChange)); } } diff --git a/src/androidTest/java/tests/integration/init/InitializationTest.java b/src/androidTest/java/tests/integration/init/InitializationTest.java index 819a0cfce..340b74889 100644 --- a/src/androidTest/java/tests/integration/init/InitializationTest.java +++ b/src/androidTest/java/tests/integration/init/InitializationTest.java @@ -187,10 +187,6 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio } private String loadSplitChanges() { - FileHelper fileHelper = new FileHelper(); - String change = fileHelper.loadFileContent(mContext, "split_changes_1.json"); - SplitChange parsedChange = Json.fromJson(change, SplitChange.class); - parsedChange.since = parsedChange.till; - return Json.toJson(parsedChange); + return IntegrationHelper.loadSplitChanges(mContext, "split_changes_1.json"); } } diff --git a/src/androidTest/java/tests/integration/matcher/UnsupportedMatcherTest.java b/src/androidTest/java/tests/integration/matcher/UnsupportedMatcherTest.java index e11af1561..98a9d70b7 100644 --- a/src/androidTest/java/tests/integration/matcher/UnsupportedMatcherTest.java +++ b/src/androidTest/java/tests/integration/matcher/UnsupportedMatcherTest.java @@ -162,10 +162,6 @@ private SplitFactory initSplitFactory(TestableSplitConfigBuilder builder, HttpCl } private String loadSplitChanges() { - FileHelper fileHelper = new FileHelper(); - String change = fileHelper.loadFileContent(mContext, "splitchanges_unsupported_matcher.json"); - SplitChange parsedChange = Json.fromJson(change, SplitChange.class); - parsedChange.since = parsedChange.till; - return Json.toJson(parsedChange); + return IntegrationHelper.loadSplitChanges(mContext, "splitchanges_unsupported_matcher.json"); } } diff --git a/src/androidTest/java/tests/integration/streaming/ControlTest.java b/src/androidTest/java/tests/integration/streaming/ControlTest.java index a2eb0cb8e..27055c780 100644 --- a/src/androidTest/java/tests/integration/streaming/ControlTest.java +++ b/src/androidTest/java/tests/integration/streaming/ControlTest.java @@ -35,6 +35,7 @@ import io.split.android.client.SplitFactory; import io.split.android.client.api.Key; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.events.SplitEvent; import io.split.android.client.network.HttpMethod; import io.split.android.client.storage.db.MySegmentEntity; @@ -276,7 +277,7 @@ private String loadSplitChanges() { SplitChange change = Json.fromJson(jsonChange, SplitChange.class); change.since = 500; change.till = 500; - return Json.toJson(change); + return Json.toJson(TargetingRulesChange.create(change)); } private void pushMySegmentMessage() { diff --git a/src/androidTest/java/tests/integration/telemetry/TelemetryIntegrationTest.java b/src/androidTest/java/tests/integration/telemetry/TelemetryIntegrationTest.java index 8f9e2490a..d843a6362 100644 --- a/src/androidTest/java/tests/integration/telemetry/TelemetryIntegrationTest.java +++ b/src/androidTest/java/tests/integration/telemetry/TelemetryIntegrationTest.java @@ -119,7 +119,7 @@ public MockResponse dispatch(RecordedRequest request) { } else if (path.contains("/splitChanges")) { long changeNumber = -1; return new MockResponse().setResponseCode(200) - .setBody("{\"splits\":[], \"since\":" + (changeNumber + 1000) + ", \"till\":" + (changeNumber + 1000) + "}"); + .setBody(IntegrationHelper.emptySplitChanges(changeNumber + 1000)); } else if (path.contains("/events/bulk")) { return new MockResponse().setResponseCode(200); } else if (path.contains("metrics/usage")) { @@ -219,7 +219,7 @@ public MockResponse dispatch(RecordedRequest request) { } else if (path.contains("/splitChanges")) { long changeNumber = -1; return new MockResponse().setResponseCode(200) - .setBody("{\"splits\":[], \"since\":" + (changeNumber + 1000) + ", \"till\":" + (changeNumber + 1000) + "}"); + .setBody(IntegrationHelper.emptySplitChanges(changeNumber + 1000)); } else if (path.contains("/events/bulk")) { return new MockResponse().setResponseCode(200); } else if (path.contains("metrics/usage")) { @@ -272,7 +272,7 @@ public MockResponse dispatch(RecordedRequest request) { } else if (path.contains("/splitChanges")) { long changeNumber = -1; return new MockResponse().setResponseCode(200) - .setBody("{\"splits\":[], \"since\":" + (changeNumber + 1000) + ", \"till\":" + (changeNumber + 1000) + "}"); + .setBody(IntegrationHelper.emptySplitChanges(changeNumber + 1000)); } else if (path.contains("/events/bulk")) { return new MockResponse().setResponseCode(200); } else if (path.contains("metrics/usage")) { @@ -393,7 +393,7 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio } else if (path.contains("/splitChanges")) { long changeNumber = -1; return new MockResponse().setResponseCode(200) - .setBody("{\"splits\":[], \"since\":" + (changeNumber + 1000) + ", \"till\":" + (changeNumber + 1000) + "}"); + .setBody(IntegrationHelper.emptySplitChanges(changeNumber + 1000)); } else if (path.contains("/events/bulk")) { return new MockResponse().setResponseCode(200); } else if (path.contains("/metrics")) { diff --git a/src/androidTest/java/tests/integration/toggle/ImpressionsToggleTest.java b/src/androidTest/java/tests/integration/toggle/ImpressionsToggleTest.java index 0cb14611d..be477b42a 100644 --- a/src/androidTest/java/tests/integration/toggle/ImpressionsToggleTest.java +++ b/src/androidTest/java/tests/integration/toggle/ImpressionsToggleTest.java @@ -278,10 +278,6 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio } private String loadSplitChanges() { - FileHelper fileHelper = new FileHelper(); - String change = fileHelper.loadFileContent(mContext, "split_changes_imp_toggle.json"); - SplitChange parsedChange = Json.fromJson(change, SplitChange.class); - parsedChange.since = parsedChange.till; - return Json.toJson(parsedChange); + return IntegrationHelper.loadSplitChanges(mContext, "split_changes_imp_toggle.json"); } } diff --git a/src/main/java/io/split/android/client/dtos/RuleBasedSegmentChange.java b/src/main/java/io/split/android/client/dtos/RuleBasedSegmentChange.java new file mode 100644 index 000000000..712d9f109 --- /dev/null +++ b/src/main/java/io/split/android/client/dtos/RuleBasedSegmentChange.java @@ -0,0 +1,29 @@ +package io.split.android.client.dtos; + +import com.google.gson.annotations.SerializedName; + +import java.util.List; + +public class RuleBasedSegmentChange { + + @SerializedName("s") + private long since; + + @SerializedName("t") + private long till; + + @SerializedName("d") + private List segments; + + public long getSince() { + return since; + } + + public long getTill() { + return till; + } + + public List getSegments() { + return segments; + } +} diff --git a/src/main/java/io/split/android/client/dtos/TargetingRulesChange.java b/src/main/java/io/split/android/client/dtos/TargetingRulesChange.java new file mode 100644 index 000000000..b2ff74867 --- /dev/null +++ b/src/main/java/io/split/android/client/dtos/TargetingRulesChange.java @@ -0,0 +1,29 @@ +package io.split.android.client.dtos; + +import androidx.annotation.VisibleForTesting; + +import com.google.gson.annotations.SerializedName; + +public class TargetingRulesChange { + @SerializedName("ff") + private SplitChange ff; + + @SerializedName("rbs") + private RuleBasedSegmentChange rbs; + + public SplitChange getFeatureFlagsChange() { + return ff; + } + + public RuleBasedSegmentChange getRuleBasedSegmentsChange() { + return rbs; + } + + @VisibleForTesting + public static TargetingRulesChange create(SplitChange splitChange) { + TargetingRulesChange targetingRulesChange = new TargetingRulesChange(); + targetingRulesChange.ff = splitChange; + targetingRulesChange.rbs = new RuleBasedSegmentChange(); + return targetingRulesChange; + } +} diff --git a/src/main/java/io/split/android/client/service/ServiceFactory.java b/src/main/java/io/split/android/client/service/ServiceFactory.java index 9af8f5093..74a9411c8 100644 --- a/src/main/java/io/split/android/client/service/ServiceFactory.java +++ b/src/main/java/io/split/android/client/service/ServiceFactory.java @@ -8,7 +8,7 @@ import io.split.android.client.dtos.AllSegmentsChange; import io.split.android.client.dtos.Event; import io.split.android.client.dtos.KeyImpression; -import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.network.HttpClient; import io.split.android.client.network.SdkTargetPath; import io.split.android.client.service.events.EventsRequestBodySerializer; @@ -23,7 +23,7 @@ import io.split.android.client.service.impressions.unique.MTK; import io.split.android.client.service.impressions.unique.MTKRequestBodySerializer; import io.split.android.client.service.mysegments.AllSegmentsResponseParser; -import io.split.android.client.service.splits.SplitChangeResponseParser; +import io.split.android.client.service.rules.TargetingRulesResponseParser; import io.split.android.client.service.sseauthentication.SseAuthenticationResponseParser; import io.split.android.client.telemetry.TelemetryConfigBodySerializer; import io.split.android.client.telemetry.TelemetryStatsBodySerializer; @@ -33,14 +33,14 @@ @RestrictTo(RestrictTo.Scope.LIBRARY) public class ServiceFactory { - public static HttpFetcher getSplitsFetcher( + public static HttpFetcher getSplitsFetcher( HttpClient httpClient, String endPoint, String splitFilterQueryString) throws URISyntaxException { return new HttpFetcherImpl<>(httpClient, SdkTargetPath.splitChanges(endPoint, splitFilterQueryString), - new SplitChangeResponseParser()); + new TargetingRulesResponseParser()); } public static HttpFetcher getMySegmentsFetcher( diff --git a/src/main/java/io/split/android/client/service/SplitApiFacade.java b/src/main/java/io/split/android/client/service/SplitApiFacade.java index bad8763c6..9f5c93b78 100644 --- a/src/main/java/io/split/android/client/service/SplitApiFacade.java +++ b/src/main/java/io/split/android/client/service/SplitApiFacade.java @@ -9,7 +9,7 @@ import io.split.android.client.dtos.AllSegmentsChange; import io.split.android.client.dtos.Event; import io.split.android.client.dtos.KeyImpression; -import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.service.http.HttpFetcher; import io.split.android.client.service.http.HttpRecorder; import io.split.android.client.service.http.mysegments.MySegmentsFetcherFactory; @@ -20,7 +20,7 @@ import io.split.android.client.telemetry.model.Stats; public class SplitApiFacade { - private final HttpFetcher mSplitFetcher; + private final HttpFetcher mSplitFetcher; private final MySegmentsFetcherFactory mMySegmentsFetcherFactory; private final HttpFetcher mSseAuthenticationFetcher; private final HttpRecorder> mEventsRecorder; @@ -30,7 +30,7 @@ public class SplitApiFacade { private final HttpRecorder mTelemetryConfigRecorder; private final HttpRecorder mTelemetryStatsRecorder; - public SplitApiFacade(@NonNull HttpFetcher splitFetcher, + public SplitApiFacade(@NonNull HttpFetcher splitFetcher, @NonNull MySegmentsFetcherFactory mySegmentsFetcherFactory, @NonNull HttpFetcher sseAuthenticationFetcher, @NonNull HttpRecorder> eventsRecorder, @@ -50,7 +50,7 @@ public SplitApiFacade(@NonNull HttpFetcher splitFetcher, mTelemetryStatsRecorder = checkNotNull(telemetryStatsRecorder); } - public HttpFetcher getSplitFetcher() { + public HttpFetcher getSplitFetcher() { return mSplitFetcher; } diff --git a/src/main/java/io/split/android/client/service/rules/TargetingRulesResponseParser.java b/src/main/java/io/split/android/client/service/rules/TargetingRulesResponseParser.java new file mode 100644 index 000000000..4ea94d9e0 --- /dev/null +++ b/src/main/java/io/split/android/client/service/rules/TargetingRulesResponseParser.java @@ -0,0 +1,22 @@ +package io.split.android.client.service.rules; + +import com.google.gson.JsonSyntaxException; + +import io.split.android.client.dtos.TargetingRulesChange; +import io.split.android.client.service.http.HttpResponseParser; +import io.split.android.client.service.http.HttpResponseParserException; +import io.split.android.client.utils.Json; + +public class TargetingRulesResponseParser implements HttpResponseParser { + + @Override + public TargetingRulesChange parse(String responseData) throws HttpResponseParserException { + try { + return Json.fromJson(responseData, TargetingRulesChange.class); + } catch (JsonSyntaxException e) { + throw new HttpResponseParserException("Syntax error parsing my segments http response: " + e.getLocalizedMessage()); + } catch (Exception e) { + throw new HttpResponseParserException("Unknown error parsing my segments http response: " + e.getLocalizedMessage()); + } + } +} diff --git a/src/main/java/io/split/android/client/service/splits/SplitsSyncHelper.java b/src/main/java/io/split/android/client/service/splits/SplitsSyncHelper.java index 17e136fb7..5b916b89e 100644 --- a/src/main/java/io/split/android/client/service/splits/SplitsSyncHelper.java +++ b/src/main/java/io/split/android/client/service/splits/SplitsSyncHelper.java @@ -13,6 +13,7 @@ import java.util.concurrent.TimeUnit; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.network.SplitHttpHeadersBuilder; import io.split.android.client.service.ServiceConstants; import io.split.android.client.service.executor.SplitTaskExecutionInfo; @@ -33,14 +34,14 @@ public class SplitsSyncHelper { private static final String TILL_PARAM = "till"; private static final int ON_DEMAND_FETCH_BACKOFF_MAX_WAIT = ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_WAIT; - private final HttpFetcher mSplitFetcher; + private final HttpFetcher mSplitFetcher; private final SplitsStorage mSplitsStorage; private final SplitChangeProcessor mSplitChangeProcessor; private final TelemetryRuntimeProducer mTelemetryRuntimeProducer; private final BackoffCounter mBackoffCounter; private final String mFlagsSpec; - public SplitsSyncHelper(@NonNull HttpFetcher splitFetcher, + public SplitsSyncHelper(@NonNull HttpFetcher splitFetcher, @NonNull SplitsStorage splitsStorage, @NonNull SplitChangeProcessor splitChangeProcessor, @NonNull TelemetryRuntimeProducer telemetryRuntimeProducer, @@ -54,7 +55,7 @@ public SplitsSyncHelper(@NonNull HttpFetcher splitFetcher, } @VisibleForTesting - public SplitsSyncHelper(@NonNull HttpFetcher splitFetcher, + public SplitsSyncHelper(@NonNull HttpFetcher splitFetcher, @NonNull SplitsStorage splitsStorage, @NonNull SplitChangeProcessor splitChangeProcessor, @NonNull TelemetryRuntimeProducer telemetryRuntimeProducer, @@ -153,7 +154,7 @@ private long fetchUntil(long till, boolean clearBeforeUpdate, boolean avoidCache return changeNumber; } - SplitChange splitChange = fetchSplits(changeNumber, avoidCache, withCdnByPass); + SplitChange splitChange = fetchSplits(changeNumber, avoidCache, withCdnByPass).getFeatureFlagsChange(); // TODO updateStorage(shouldClearBeforeUpdate, splitChange); shouldClearBeforeUpdate = false; @@ -164,7 +165,7 @@ private long fetchUntil(long till, boolean clearBeforeUpdate, boolean avoidCache } } - private SplitChange fetchSplits(long till, boolean avoidCache, boolean withCdnByPass) throws HttpFetcherException { + private TargetingRulesChange fetchSplits(long till, boolean avoidCache, boolean withCdnByPass) throws HttpFetcherException { Map params = new LinkedHashMap<>(); if (mFlagsSpec != null && !mFlagsSpec.trim().isEmpty()) { params.put(FLAGS_SPEC_PARAM, mFlagsSpec); diff --git a/src/main/java/io/split/android/client/service/workmanager/splits/FetcherProvider.java b/src/main/java/io/split/android/client/service/workmanager/splits/FetcherProvider.java index dd572e48a..e234f487f 100644 --- a/src/main/java/io/split/android/client/service/workmanager/splits/FetcherProvider.java +++ b/src/main/java/io/split/android/client/service/workmanager/splits/FetcherProvider.java @@ -2,7 +2,7 @@ import java.net.URISyntaxException; -import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.network.HttpClient; import io.split.android.client.service.ServiceFactory; import io.split.android.client.service.http.HttpFetcher; @@ -17,7 +17,7 @@ class FetcherProvider { mEndpoint = endpoint; } - public HttpFetcher provideFetcher(String splitsFilterQueryString) throws URISyntaxException { + public HttpFetcher provideFetcher(String splitsFilterQueryString) throws URISyntaxException { return ServiceFactory.getSplitsFetcher(mHttpClient, mEndpoint, splitsFilterQueryString); } } diff --git a/src/main/java/io/split/android/client/service/workmanager/splits/SyncHelperProvider.java b/src/main/java/io/split/android/client/service/workmanager/splits/SyncHelperProvider.java index 427b690e5..83cec1c58 100644 --- a/src/main/java/io/split/android/client/service/workmanager/splits/SyncHelperProvider.java +++ b/src/main/java/io/split/android/client/service/workmanager/splits/SyncHelperProvider.java @@ -1,6 +1,6 @@ package io.split.android.client.service.workmanager.splits; -import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.service.http.HttpFetcher; import io.split.android.client.service.splits.SplitChangeProcessor; import io.split.android.client.service.splits.SplitsSyncHelper; @@ -9,10 +9,10 @@ class SyncHelperProvider { - SplitsSyncHelper provideSplitsSyncHelper(HttpFetcher splitsFetcher, SplitsStorage splitsStorage, - SplitChangeProcessor mSplitChangeProcessor, - TelemetryStorage telemetryStorage, - String mFlagsSpec) { + SplitsSyncHelper provideSplitsSyncHelper(HttpFetcher splitsFetcher, SplitsStorage splitsStorage, + SplitChangeProcessor mSplitChangeProcessor, + TelemetryStorage telemetryStorage, + String mFlagsSpec) { return new SplitsSyncHelper(splitsFetcher, splitsStorage, mSplitChangeProcessor, telemetryStorage, diff --git a/src/main/java/io/split/android/engine/experiments/ParserCommons.java b/src/main/java/io/split/android/engine/experiments/ParserCommons.java index 8bfdcd6e8..aeb5d1259 100644 --- a/src/main/java/io/split/android/engine/experiments/ParserCommons.java +++ b/src/main/java/io/split/android/engine/experiments/ParserCommons.java @@ -46,7 +46,8 @@ public class ParserCommons { - public static final int CONDITIONS_UPPER_LIMIT = 50; + private static final int CONDITIONS_UPPER_LIMIT = 50; + private final MySegmentsStorageContainer mMySegmentsStorageContainer; private final MySegmentsStorageContainer mMyLargeSegmentsStorageContainer; private RuleBasedSegmentStorage mRuleBasedSegmentStorage; diff --git a/src/test/java/io/split/android/client/service/HttpFetcherTest.java b/src/test/java/io/split/android/client/service/HttpFetcherTest.java index 36c85ffe0..7c3e5775d 100644 --- a/src/test/java/io/split/android/client/service/HttpFetcherTest.java +++ b/src/test/java/io/split/android/client/service/HttpFetcherTest.java @@ -27,6 +27,7 @@ import io.split.android.client.dtos.AllSegmentsChange; import io.split.android.client.dtos.SegmentsChange; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.network.HttpClient; import io.split.android.client.network.HttpException; import io.split.android.client.network.HttpMethod; @@ -40,7 +41,7 @@ import io.split.android.client.service.http.HttpFetcherImpl; import io.split.android.client.service.http.HttpResponseParser; import io.split.android.client.service.mysegments.AllSegmentsResponseParser; -import io.split.android.client.service.splits.SplitChangeResponseParser; +import io.split.android.client.service.rules.TargetingRulesResponseParser; public class HttpFetcherTest { @@ -52,7 +53,7 @@ public class HttpFetcherTest { private URI mUrl; private URI mSplitChangesUrl; private URI mMySegmentsUrl; - private final HttpResponseParser mSplitChangeResponseParser = new SplitChangeResponseParser(); + private final HttpResponseParser mSplitChangeResponseParser = new TargetingRulesResponseParser(); private final HttpResponseParser mMySegmentsResponseParser = new AllSegmentsResponseParser(); @Before @@ -67,7 +68,7 @@ public void setup() throws URISyntaxException { public void testNoReachableUrl() throws URISyntaxException { - HttpFetcher fetcher = new HttpFetcherImpl<>(mClientMock, mUrl, mSplitChangeResponseParser); + HttpFetcher fetcher = new HttpFetcherImpl<>(mClientMock, mUrl, mSplitChangeResponseParser); boolean isReachable = true; try { Map params = new HashMap<>(); @@ -90,12 +91,12 @@ public void testSuccessfulSplitChangeFetch() throws URISyntaxException, HttpExce when(request.execute()).thenReturn(response); when(mClientMock.request(uri, HttpMethod.GET, null, null)).thenReturn(request); - HttpFetcher fetcher = new HttpFetcherImpl<>(mClientMock, mSplitChangesUrl, mSplitChangeResponseParser); + HttpFetcher fetcher = new HttpFetcherImpl<>(mClientMock, mSplitChangesUrl, mSplitChangeResponseParser); SplitChange change = null; try { Map params = new HashMap<>(); params.put("since", -1); - change = fetcher.execute(params, null); + change = fetcher.execute(params, null).getFeatureFlagsChange(); // TODO } catch (HttpFetcherException e) { exceptionWasThrown = true; } @@ -116,22 +117,24 @@ public void tesNonEmptyHeaderSplitChangesFetch() throws URISyntaxException, Http URI uri = new URIBuilder(mSplitChangesUrl).addParameter("since", "" + -1).build(); HttpRequest request = mock(HttpRequest.class); - HttpResponse response = new HttpResponseImpl(200, dummySplitChangeResponse()); + when(request.execute()).thenReturn(response); when(mClientMock.request(uri, HttpMethod.GET, null, headers)).thenReturn(request); - HttpFetcher fetcher = new HttpFetcherImpl<>(mClientMock, mSplitChangesUrl, mSplitChangeResponseParser); + HttpFetcher fetcher = new HttpFetcherImpl<>(mClientMock, mSplitChangesUrl, mSplitChangeResponseParser); try { Map params = new HashMap<>(); params.put("since", -1); - SplitChange change = fetcher.execute(params, headers); + SplitChange change = fetcher.execute(params, headers).getFeatureFlagsChange(); // TODO } catch (HttpFetcherException e) { + e.printStackTrace(); exceptionWasThrown = true; } ArgumentCaptor> headersCaptor = ArgumentCaptor.forClass(Map.class); + Assert.assertFalse(exceptionWasThrown); verify(mClientMock).request(eq(uri), eq(HttpMethod.GET), eq(null), headersCaptor.capture()); Assert.assertEquals("value1", headersCaptor.getValue().get("header1")); } @@ -145,13 +148,13 @@ public void testFailedResponse() throws URISyntaxException, HttpException { when(request.execute()).thenReturn(response); when(mClientMock.request(uri, HttpMethod.GET, null, null)).thenReturn(request); - HttpFetcher fetcher = new HttpFetcherImpl<>(mClientMock, mSplitChangesUrl, mSplitChangeResponseParser); + HttpFetcher fetcher = new HttpFetcherImpl<>(mClientMock, mSplitChangesUrl, mSplitChangeResponseParser); SplitChange change = null; boolean failed = false; try { Map params = new HashMap<>(); params.put("since", -1); - change = fetcher.execute(params, null); + change = fetcher.execute(params, null).getFeatureFlagsChange(); // TODO } catch (HttpFetcherException e) { failed = true; } @@ -169,13 +172,13 @@ public void testWrongResponse() throws URISyntaxException, HttpException { when(request.execute()).thenReturn(response); when(mClientMock.request(uri, HttpMethod.GET, null, null)).thenReturn(request); - HttpFetcher fetcher = new HttpFetcherImpl<>(mClientMock, mSplitChangesUrl, mSplitChangeResponseParser); + HttpFetcher fetcher = new HttpFetcherImpl<>(mClientMock, mSplitChangesUrl, mSplitChangeResponseParser); SplitChange change = null; boolean failed = false; try { Map params = new HashMap<>(); params.put("since", -1); - change = fetcher.execute(params, null); + change = fetcher.execute(params, null).getFeatureFlagsChange(); // TODO } catch (HttpFetcherException e) { failed = true; } @@ -261,7 +264,7 @@ public void testHandleParserExceptionFetch() throws URISyntaxException, HttpExce @Test public void paramOrderIsCorrect() throws HttpFetcherException, HttpException { - HttpFetcher fetcher = getSplitChangeHttpFetcher(); + HttpFetcher fetcher = getSplitChangeHttpFetcher(); Map params = new LinkedHashMap<>(); params.put("s", "1.1"); @@ -276,7 +279,7 @@ public void paramOrderIsCorrect() throws HttpFetcherException, HttpException { @Test public void paramOrderWithoutTillIsCorrect() throws HttpException, HttpFetcherException { - HttpFetcher fetcher = getSplitChangeHttpFetcher(); + HttpFetcher fetcher = getSplitChangeHttpFetcher(); Map params = new LinkedHashMap<>(); params.put("s", "1.1"); @@ -290,7 +293,7 @@ public void paramOrderWithoutTillIsCorrect() throws HttpException, HttpFetcherEx @Test public void paramOrderWithoutSpecIsCorrect() throws HttpException, HttpFetcherException { - HttpFetcher fetcher = getSplitChangeHttpFetcher(); + HttpFetcher fetcher = getSplitChangeHttpFetcher(); Map params = new LinkedHashMap<>(); params.put("since", "-1"); @@ -304,7 +307,7 @@ public void paramOrderWithoutSpecIsCorrect() throws HttpException, HttpFetcherEx @Test public void paramOrderWithoutSetsIsCorrect() throws HttpException { - HttpFetcher fetcher = getSplitChangeHttpFetcher(); + HttpFetcher fetcher = getSplitChangeHttpFetcher(); Map params = new LinkedHashMap<>(); params.put("till", "100"); @@ -322,7 +325,7 @@ public void paramOrderWithoutSetsIsCorrect() throws HttpException { @Test public void httpExceptionWithStatusCodeAddsStatusCodeToHttpFetcherException() throws HttpException { - HttpFetcher fetcher = getSplitChangeHttpFetcher(); + HttpFetcher fetcher = getSplitChangeHttpFetcher(); HttpRequest request = mock(HttpRequest.class); when(request.execute()).thenThrow(new HttpException("Not found", 404)); @@ -341,12 +344,12 @@ public void httpExceptionWithStatusCodeAddsStatusCodeToHttpFetcherException() th } @NonNull - private HttpFetcher getSplitChangeHttpFetcher() throws HttpException { + private HttpFetcher getSplitChangeHttpFetcher() throws HttpException { HttpRequest mockRequest = mock(HttpRequest.class); when(mockRequest.execute()).thenReturn(new HttpResponseImpl(200, dummySplitChangeResponse())); when(mClientMock.request(any(), any(), any(), any())).thenReturn(mockRequest); - HttpFetcher fetcher = new HttpFetcherImpl<>(mClientMock, mSplitChangesUrl, mSplitChangeResponseParser); + HttpFetcher fetcher = new HttpFetcherImpl<>(mClientMock, mSplitChangesUrl, mSplitChangeResponseParser); return fetcher; } @@ -361,9 +364,26 @@ public boolean matches(URI argument) { } private String dummySplitChangeResponse() { - return "{\"splits\":[{\"name\":\"sample_feature\", \"status\":\"ACTIVE\"}],\n" + - " \"since\":-1,\n" + - " \"till\":100}"; +// return "{\"splits\":[{\"name\":\"sample_feature\", \"status\":\"ACTIVE\"}],\n" + +// " \"since\":-1,\n" + +// " \"till\":100}"; + return "{\n" + + " \"ff\": {\n" + + " \"splits\": [\n" + + " {\n" + + " \"name\": \"sample_feature\",\n" + + " \"status\": \"ACTIVE\"\n" + + " }\n" + + " ],\n" + + " \"since\": -1,\n" + + " \"till\": 100\n" + + " },\n" + + " \"rbs\": {\n" + + " \"d\": [],\n" + + " \"s\": -1,\n" + + " \"t\": 50\n" + + " }\n" + + "}"; } private String dummyMySegmentsResponse() { diff --git a/src/test/java/io/split/android/client/service/SplitsSyncHelperTest.java b/src/test/java/io/split/android/client/service/SplitsSyncHelperTest.java index 2ff74c234..066c3c1bf 100644 --- a/src/test/java/io/split/android/client/service/SplitsSyncHelperTest.java +++ b/src/test/java/io/split/android/client/service/SplitsSyncHelperTest.java @@ -28,6 +28,7 @@ import java.util.Map; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.network.SplitHttpHeadersBuilder; import io.split.android.client.service.executor.SplitTaskExecutionInfo; import io.split.android.client.service.executor.SplitTaskExecutionStatus; @@ -45,10 +46,10 @@ public class SplitsSyncHelperTest { @Mock - HttpFetcher mSplitsFetcher; + HttpFetcher mSplitsFetcher; @Mock SplitsStorage mSplitsStorage; - SplitChange mSplitChange = null; + TargetingRulesChange mTargetingRulesChange = null; @Spy SplitChangeProcessor mSplitChangeProcessor; @Mock @@ -87,17 +88,17 @@ public void tearDown() { public void correctSyncExecution() throws HttpFetcherException { // On correct execution without having clear param // should execute fetcher, update storage and avoid clearing splits cache - when(mSplitsFetcher.execute(mDefaultParams, null)).thenReturn(mSplitChange); - SplitChange secondSplitChange = mSplitChange; - secondSplitChange.since = mSplitChange.till; - when(mSplitsFetcher.execute(mSecondFetchParams, null)).thenReturn(secondSplitChange); + when(mSplitsFetcher.execute(mDefaultParams, null)).thenReturn(mTargetingRulesChange); + SplitChange secondSplitChange = mTargetingRulesChange.getFeatureFlagsChange(); // TODO + secondSplitChange.since = mTargetingRulesChange.getFeatureFlagsChange().till; + when(mSplitsFetcher.execute(mSecondFetchParams, null)).thenReturn(TargetingRulesChange.create(secondSplitChange)); when(mSplitsStorage.getTill()).thenReturn(-1L); SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(-1, false, false, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES); verify(mSplitsFetcher, times(1)).execute(mDefaultParams, null); verify(mSplitsStorage, times(1)).update(any()); - verify(mSplitChangeProcessor, times(1)).process(mSplitChange); + verify(mSplitChangeProcessor, times(1)).process(mTargetingRulesChange.getFeatureFlagsChange()); // TODO verify(mSplitsStorage, never()).clear(); assertEquals(SplitTaskExecutionStatus.SUCCESS, result.getStatus()); } @@ -109,17 +110,17 @@ public void correctSyncExecutionNoCache() throws HttpFetcherException { Map headers = new HashMap<>(); headers.put(SplitHttpHeadersBuilder.CACHE_CONTROL_HEADER, SplitHttpHeadersBuilder.CACHE_CONTROL_NO_CACHE); - when(mSplitsFetcher.execute(mDefaultParams, headers)).thenReturn(mSplitChange); - SplitChange secondSplitChange = mSplitChange; - secondSplitChange.since = mSplitChange.till; - when(mSplitsFetcher.execute(mSecondFetchParams, null)).thenReturn(secondSplitChange); + when(mSplitsFetcher.execute(mDefaultParams, headers)).thenReturn(mTargetingRulesChange); + SplitChange secondSplitChange = mTargetingRulesChange.getFeatureFlagsChange(); // TODO + secondSplitChange.since = mTargetingRulesChange.getFeatureFlagsChange().till; + when(mSplitsFetcher.execute(mSecondFetchParams, null)).thenReturn(TargetingRulesChange.create(secondSplitChange)); when(mSplitsStorage.getTill()).thenReturn(-1L); SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(-1, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES); verify(mSplitsFetcher, times(1)).execute(mDefaultParams, headers); verify(mSplitsStorage, times(1)).update(any()); - verify(mSplitChangeProcessor, times(1)).process(mSplitChange); + verify(mSplitChangeProcessor, times(1)).process(mTargetingRulesChange.getFeatureFlagsChange()); // TODO verify(mSplitsStorage, never()).clear(); assertEquals(SplitTaskExecutionStatus.SUCCESS, result.getStatus()); } @@ -135,13 +136,13 @@ public void fetcherSyncException() throws HttpFetcherException { verify(mSplitsFetcher, times(1)).execute(mDefaultParams, null); verify(mSplitsStorage, never()).update(any()); verify(mSplitsStorage, never()).clear(); - verify(mSplitChangeProcessor, never()).process(mSplitChange); + verify(mSplitChangeProcessor, never()).process(mTargetingRulesChange.getFeatureFlagsChange()); // TODO assertEquals(SplitTaskExecutionStatus.ERROR, result.getStatus()); } @Test public void storageException() throws HttpFetcherException { - when(mSplitsFetcher.execute(mDefaultParams, null)).thenReturn(mSplitChange); + when(mSplitsFetcher.execute(mDefaultParams, null)).thenReturn(mTargetingRulesChange); doThrow(NullPointerException.class).when(mSplitsStorage).update(any(ProcessedSplitChange.class)); when(mSplitsStorage.getTill()).thenReturn(-1L); @@ -150,17 +151,17 @@ public void storageException() throws HttpFetcherException { verify(mSplitsFetcher, times(1)).execute(mDefaultParams, null); verify(mSplitsStorage, times(1)).update(any()); verify(mSplitsStorage, times(1)).clear(); - verify(mSplitChangeProcessor, times(1)).process(mSplitChange); + verify(mSplitChangeProcessor, times(1)).process(mTargetingRulesChange.getFeatureFlagsChange()); // TODO assertEquals(SplitTaskExecutionStatus.ERROR, result.getStatus()); } @Test public void shouldClearStorageAfterFetch() throws HttpFetcherException { - when(mSplitsFetcher.execute(mDefaultParams, null)).thenReturn(mSplitChange); - SplitChange secondSplitChange = mSplitChange; - secondSplitChange.since = mSplitChange.till; - when(mSplitsFetcher.execute(mSecondFetchParams, null)).thenReturn(secondSplitChange); + when(mSplitsFetcher.execute(mDefaultParams, null)).thenReturn(mTargetingRulesChange); + SplitChange secondSplitChange = mTargetingRulesChange.getFeatureFlagsChange(); // TODO + secondSplitChange.since = mTargetingRulesChange.getFeatureFlagsChange().till; + when(mSplitsFetcher.execute(mSecondFetchParams, null)).thenReturn(TargetingRulesChange.create(secondSplitChange)); when(mSplitsStorage.getTill()).thenReturn(-1L); SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(-1, true, false, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES); @@ -168,7 +169,7 @@ public void shouldClearStorageAfterFetch() throws HttpFetcherException { verify(mSplitsFetcher, times(1)).execute(mDefaultParams, null); verify(mSplitsStorage, times(1)).update(any()); verify(mSplitsStorage, times(1)).clear(); - verify(mSplitChangeProcessor, times(1)).process(mSplitChange); + verify(mSplitChangeProcessor, times(1)).process(mTargetingRulesChange.getFeatureFlagsChange()); // TODO assertEquals(SplitTaskExecutionStatus.SUCCESS, result.getStatus()); } @@ -186,9 +187,9 @@ public void errorIsRecordedInTelemetry() throws HttpFetcherException { @Test public void performSplitsFetchUntilSinceEqualsTill() throws HttpFetcherException { - SplitChange firstSplitChange = getSplitChange(-1, 2); - SplitChange secondSplitChange = getSplitChange(2, 3); - SplitChange thirdSplitChange = getSplitChange(3, 3); + TargetingRulesChange firstSplitChange = getSplitChange(-1, 2); + TargetingRulesChange secondSplitChange = getSplitChange(2, 3); + TargetingRulesChange thirdSplitChange = getSplitChange(3, 3); Map firstParams = getSinceParams(-1L); Map secondParams = getSinceParams(2L); Map thirdParams = getSinceParams(3L); @@ -209,15 +210,15 @@ public void performSplitsFetchUntilSinceEqualsTill() throws HttpFetcherException @Test public void performSplitFetchUntilStoredChangeNumberIsGreaterThanRequested() throws HttpFetcherException { - SplitChange firstSplitChange = getSplitChange(-1, 2); - SplitChange secondSplitChange = getSplitChange(2, 4); + SplitChange firstSplitChange = getSplitChange(-1, 2).getFeatureFlagsChange(); + SplitChange secondSplitChange = getSplitChange(2, 4).getFeatureFlagsChange(); Map firstParams = getSinceParams(-1L); Map secondParams = getSinceParams(2L); when(mSplitsStorage.getTill()).thenReturn(-1L, 2L, 4L); - when(mSplitsFetcher.execute(eq(firstParams), any())).thenReturn(firstSplitChange); - when(mSplitsFetcher.execute(eq(secondParams), any())).thenReturn(secondSplitChange); + when(mSplitsFetcher.execute(eq(firstParams), any())).thenReturn(TargetingRulesChange.create(firstSplitChange)); + when(mSplitsFetcher.execute(eq(secondParams), any())).thenReturn(TargetingRulesChange.create(secondSplitChange)); mSplitsSyncHelper.sync(3, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES); @@ -227,7 +228,8 @@ public void performSplitFetchUntilStoredChangeNumberIsGreaterThanRequested() thr } @Test - public void syncWithClearBeforeUpdateOnlyClearsStorageOnce() { + public void syncWithClearBeforeUpdateOnlyClearsStorageOnce() throws HttpFetcherException { + when(mSplitsFetcher.execute(mDefaultParams, null)).thenReturn(mTargetingRulesChange); when(mSplitsStorage.getTill()).thenReturn(-1L, 2L, 4L); mSplitsSyncHelper.sync(3, true, false, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES); @@ -367,9 +369,9 @@ public boolean matches(Map argument) { } private void loadSplitChanges() { - if (mSplitChange == null) { + if (mTargetingRulesChange == null) { FileHelper fileHelper = new FileHelper(); - mSplitChange = fileHelper.loadSplitChangeFromFile("split_changes_1.json"); + mTargetingRulesChange = TargetingRulesChange.create(fileHelper.loadSplitChangeFromFile("split_changes_1.json")); } } @@ -381,12 +383,12 @@ private Map getSinceParams(long since) { return params; } - private SplitChange getSplitChange(int since, int till) { + private TargetingRulesChange getSplitChange(int since, int till) { SplitChange splitChange = new SplitChange(); splitChange.since = since; splitChange.till = till; splitChange.splits = new ArrayList<>(); - return splitChange; + return TargetingRulesChange.create(splitChange); } } diff --git a/src/test/java/io/split/android/client/service/SynchronizerTest.java b/src/test/java/io/split/android/client/service/SynchronizerTest.java index 79347551f..5943b5f14 100644 --- a/src/test/java/io/split/android/client/service/SynchronizerTest.java +++ b/src/test/java/io/split/android/client/service/SynchronizerTest.java @@ -42,7 +42,7 @@ import io.split.android.client.api.Key; import io.split.android.client.dtos.Event; import io.split.android.client.dtos.KeyImpression; -import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.events.SplitEventsManager; import io.split.android.client.impressions.DecoratedImpression; import io.split.android.client.impressions.Impression; @@ -163,7 +163,7 @@ public void setup(SplitClientConfig splitClientConfig, ImpressionManagerConfig.M mTaskExecutor = taskExecutor; mSingleThreadedTaskExecutor = spy(new SplitTaskExecutorStub()); - HttpFetcher splitsFetcher = Mockito.mock(HttpFetcher.class); + HttpFetcher splitsFetcher = Mockito.mock(HttpFetcher.class); HttpFetcher mySegmentsFetcher = Mockito.mock(HttpFetcher.class); HttpRecorder> eventsRecorder = Mockito.mock(HttpRecorder.class); HttpRecorder> impressionsRecorder = Mockito.mock(HttpRecorder.class); diff --git a/src/test/java/io/split/android/client/service/rules/TargetingRulesResponseParserTest.java b/src/test/java/io/split/android/client/service/rules/TargetingRulesResponseParserTest.java new file mode 100644 index 000000000..9af50d80d --- /dev/null +++ b/src/test/java/io/split/android/client/service/rules/TargetingRulesResponseParserTest.java @@ -0,0 +1,5 @@ +package io.split.android.client.service.rules; + +public class TargetingRulesResponseParserTest { + +} diff --git a/src/test/java/io/split/android/client/service/workmanager/splits/SplitsSyncWorkerTaskBuilderTest.java b/src/test/java/io/split/android/client/service/workmanager/splits/SplitsSyncWorkerTaskBuilderTest.java index 1b950b365..d0caec362 100644 --- a/src/test/java/io/split/android/client/service/workmanager/splits/SplitsSyncWorkerTaskBuilderTest.java +++ b/src/test/java/io/split/android/client/service/workmanager/splits/SplitsSyncWorkerTaskBuilderTest.java @@ -13,7 +13,7 @@ import java.net.URISyntaxException; -import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.service.executor.SplitTask; import io.split.android.client.service.http.HttpFetcher; import io.split.android.client.service.splits.SplitChangeProcessor; @@ -29,7 +29,7 @@ public class SplitsSyncWorkerTaskBuilderTest { private SplitChangeProcessor mSplitChangeProcessor; private SyncHelperProvider mSplitsSyncHelperProvider; private SplitsStorage mSplitsStorage; - private HttpFetcher mSplitsFetcher; + private HttpFetcher mSplitsFetcher; private TelemetryStorage mTelemetryStorage; @Before From fa221707b4fe2b66e7f6867709175a6634bc73ae Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Wed, 5 Mar 2025 10:32:28 -0300 Subject: [PATCH 21/26] Manual test updates --- src/androidTest/java/tests/integration/IntegrationTest.java | 2 +- .../integration/largesegments/LargeSegmentTestHelper.java | 3 ++- .../integration/largesegments/LargeSegmentsStreamingTest.java | 3 ++- .../java/tests/integration/sets/FlagSetsEvaluationTest.java | 3 ++- .../tests/integration/sets/FlagSetsMultipleFactoryTest.java | 3 ++- .../java/tests/integration/sets/FlagSetsPollingTest.java | 3 ++- 6 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/androidTest/java/tests/integration/IntegrationTest.java b/src/androidTest/java/tests/integration/IntegrationTest.java index 2b872204f..07f8d8c37 100644 --- a/src/androidTest/java/tests/integration/IntegrationTest.java +++ b/src/androidTest/java/tests/integration/IntegrationTest.java @@ -410,7 +410,7 @@ private String splitsPerRequest(int reqId) { if (reqId < req) { req = reqId; } - return "{\"ff\":" + mJsonChanges.get(req) + "\"}"; + return mJsonChanges.get(req); } private void loadSplitChanges() { diff --git a/src/androidTest/java/tests/integration/largesegments/LargeSegmentTestHelper.java b/src/androidTest/java/tests/integration/largesegments/LargeSegmentTestHelper.java index f8d876a69..95cbf86bf 100644 --- a/src/androidTest/java/tests/integration/largesegments/LargeSegmentTestHelper.java +++ b/src/androidTest/java/tests/integration/largesegments/LargeSegmentTestHelper.java @@ -23,6 +23,7 @@ import io.split.android.client.SplitClient; import io.split.android.client.SplitFactory; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.events.SplitEvent; import io.split.android.client.storage.db.SplitRoomDatabase; import io.split.android.client.utils.Json; @@ -155,6 +156,6 @@ private String splitChangesLargeSegments(long since, long till) { parsedChange.since = since; parsedChange.till = till; - return Json.toJson(parsedChange); + return Json.toJson(TargetingRulesChange.create(parsedChange)); } } diff --git a/src/androidTest/java/tests/integration/largesegments/LargeSegmentsStreamingTest.java b/src/androidTest/java/tests/integration/largesegments/LargeSegmentsStreamingTest.java index f5cfe3cdc..c5bc39732 100644 --- a/src/androidTest/java/tests/integration/largesegments/LargeSegmentsStreamingTest.java +++ b/src/androidTest/java/tests/integration/largesegments/LargeSegmentsStreamingTest.java @@ -33,6 +33,7 @@ import io.split.android.client.SplitFactory; import io.split.android.client.dtos.SegmentsChange; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.events.SplitEvent; import io.split.android.client.storage.db.SplitRoomDatabase; import io.split.android.client.utils.Json; @@ -189,7 +190,7 @@ private String splitChangesLargeSegments(long since, long till) { parsedChange.since = since; parsedChange.till = till; - return Json.toJson(parsedChange); + return Json.toJson(TargetingRulesChange.create(parsedChange)); } private void initializeLatches() { diff --git a/src/androidTest/java/tests/integration/sets/FlagSetsEvaluationTest.java b/src/androidTest/java/tests/integration/sets/FlagSetsEvaluationTest.java index 5507e432b..0f77dbc92 100644 --- a/src/androidTest/java/tests/integration/sets/FlagSetsEvaluationTest.java +++ b/src/androidTest/java/tests/integration/sets/FlagSetsEvaluationTest.java @@ -31,6 +31,7 @@ import io.split.android.client.SplitResult; import io.split.android.client.SyncConfig; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.events.SplitEvent; import io.split.android.client.storage.db.SplitRoomDatabase; import io.split.android.client.utils.Json; @@ -166,6 +167,6 @@ private String loadSplitChangeWithSet(int setsCount) { SplitChange parsedChange = Json.fromJson(change, SplitChange.class); parsedChange.since = parsedChange.till; - return Json.toJson(parsedChange); + return Json.toJson(TargetingRulesChange.create(parsedChange)); } } diff --git a/src/androidTest/java/tests/integration/sets/FlagSetsMultipleFactoryTest.java b/src/androidTest/java/tests/integration/sets/FlagSetsMultipleFactoryTest.java index da3d18250..c5e0609c2 100644 --- a/src/androidTest/java/tests/integration/sets/FlagSetsMultipleFactoryTest.java +++ b/src/androidTest/java/tests/integration/sets/FlagSetsMultipleFactoryTest.java @@ -35,6 +35,7 @@ import io.split.android.client.SplitFilter; import io.split.android.client.SyncConfig; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.events.SplitEvent; import io.split.android.client.utils.Json; import tests.integration.shared.TestingHelper; @@ -128,7 +129,7 @@ private String loadSplitChangeWithSet(int setsCount) { SplitChange parsedChange = Json.fromJson(change, SplitChange.class); parsedChange.since = parsedChange.till; - return Json.toJson(parsedChange); + return Json.toJson(TargetingRulesChange.create(parsedChange)); } private HttpResponseMockDispatcher getDispatcher(int setsCount) { diff --git a/src/androidTest/java/tests/integration/sets/FlagSetsPollingTest.java b/src/androidTest/java/tests/integration/sets/FlagSetsPollingTest.java index 06146259a..b7ddb3169 100644 --- a/src/androidTest/java/tests/integration/sets/FlagSetsPollingTest.java +++ b/src/androidTest/java/tests/integration/sets/FlagSetsPollingTest.java @@ -34,6 +34,7 @@ import io.split.android.client.SyncConfig; import io.split.android.client.TestingConfig; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.storage.db.SplitEntity; import io.split.android.client.storage.db.SplitRoomDatabase; import io.split.android.client.utils.Json; @@ -229,6 +230,6 @@ private String loadSplitChangeWithSet(int setsCount) { SplitChange parsedChange = Json.fromJson(change, SplitChange.class); parsedChange.since = parsedChange.till; - return Json.toJson(parsedChange); + return Json.toJson(TargetingRulesChange.create(parsedChange)); } } From 0aabcaa52d60f539ec2074bf198568b47b36f398 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Wed, 5 Mar 2025 10:46:14 -0300 Subject: [PATCH 22/26] Change mock .json --- .../assets/split_changes_flag_set-0.json | 2 +- .../assets/split_changes_flag_set-1.json | 2 +- .../assets/split_changes_flag_set-2.json | 2 +- .../assets/split_changes_imp_toggle.json | 243 +- .../split_changes_large_segments-0.json | 8 +- .../assets/split_changes_semver.json | 799 +-- .../assets/splitchanges_int_test.json | 84 +- .../splitchanges_unsupported_matcher.json | 170 +- src/test/resources/split_changes_1.json | 4889 +++++++++-------- .../resources/split_changes_1_updated.json | 201 + 10 files changed, 3319 insertions(+), 3081 deletions(-) create mode 100644 src/test/resources/split_changes_1_updated.json diff --git a/src/androidTest/assets/split_changes_flag_set-0.json b/src/androidTest/assets/split_changes_flag_set-0.json index 93be5fda4..9a6c1831b 100644 --- a/src/androidTest/assets/split_changes_flag_set-0.json +++ b/src/androidTest/assets/split_changes_flag_set-0.json @@ -1 +1 @@ -{"splits":[{"trafficTypeName":"client","name":"workm","trafficAllocation":100,"trafficAllocationSeed":147392224,"seed":524417105,"status":"ACTIVE","killed":false,"defaultTreatment":"on","changeNumber":1602798638344,"algo":2,"configurations":{},"sets":["set_3"],"conditions":[{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","attribute":null},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"new_segment"},"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":0},{"treatment":"free","size":100},{"treatment":"conta","size":0}],"label":"in segment new_segment"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","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":100},{"treatment":"off","size":0},{"treatment":"free","size":0},{"treatment":"conta","size":0}],"label":"default rule"}]}],"since":1602797638344,"till":1602798638344} +{"ff":{"splits":[{"trafficTypeName":"client","name":"workm","trafficAllocation":100,"trafficAllocationSeed":147392224,"seed":524417105,"status":"ACTIVE","killed":false,"defaultTreatment":"on","changeNumber":1602798638344,"algo":2,"configurations":{},"sets":["set_3"],"conditions":[{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","attribute":null},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"new_segment"},"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":0},{"treatment":"free","size":100},{"treatment":"conta","size":0}],"label":"in segment new_segment"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","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":100},{"treatment":"off","size":0},{"treatment":"free","size":0},{"treatment":"conta","size":0}],"label":"default rule"}]}],"since":1602797638344,"till":1602798638344},"rbs":{"d":[],"s":1602798638341,"t":1602798638341}} diff --git a/src/androidTest/assets/split_changes_flag_set-1.json b/src/androidTest/assets/split_changes_flag_set-1.json index 67f617712..14ba0e90d 100644 --- a/src/androidTest/assets/split_changes_flag_set-1.json +++ b/src/androidTest/assets/split_changes_flag_set-1.json @@ -1 +1 @@ -{"splits":[{"trafficTypeName":"client","name":"workm","trafficAllocation":100,"trafficAllocationSeed":147392224,"seed":524417105,"status":"ACTIVE","killed":false,"defaultTreatment":"on","changeNumber":1602797638344,"algo":2,"configurations":{},"sets":["set_1"],"conditions":[{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","attribute":null},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"new_segment"},"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":0},{"treatment":"free","size":100},{"treatment":"conta","size":0}],"label":"in segment new_segment"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","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":100},{"treatment":"off","size":0},{"treatment":"free","size":0},{"treatment":"conta","size":0}],"label":"default rule"}]}],"since":1602796638344,"till":1602797638344} +{"ff":{"splits":[{"trafficTypeName":"client","name":"workm","trafficAllocation":100,"trafficAllocationSeed":147392224,"seed":524417105,"status":"ACTIVE","killed":false,"defaultTreatment":"on","changeNumber":1602797638344,"algo":2,"configurations":{},"sets":["set_1"],"conditions":[{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","attribute":null},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"new_segment"},"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":0},{"treatment":"free","size":100},{"treatment":"conta","size":0}],"label":"in segment new_segment"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","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":100},{"treatment":"off","size":0},{"treatment":"free","size":0},{"treatment":"conta","size":0}],"label":"default rule"}]}],"since":1602796638344,"till":1602797638344},"rbs":{"d":[],"s":1602796638341,"t":1602796638341}} diff --git a/src/androidTest/assets/split_changes_flag_set-2.json b/src/androidTest/assets/split_changes_flag_set-2.json index 0e7576af6..8d1c23d6a 100644 --- a/src/androidTest/assets/split_changes_flag_set-2.json +++ b/src/androidTest/assets/split_changes_flag_set-2.json @@ -1 +1 @@ -{"splits":[{"trafficTypeName":"client","name":"workm","trafficAllocation":100,"trafficAllocationSeed":147392224,"seed":524417105,"status":"ACTIVE","killed":false,"defaultTreatment":"on","changeNumber":1602796638344,"algo":2,"configurations":{},"sets":["set_1","set_2"],"conditions":[{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","attribute":null},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"new_segment"},"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":0},{"treatment":"free","size":100},{"treatment":"conta","size":0}],"label":"in segment new_segment"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","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":100},{"treatment":"off","size":0},{"treatment":"free","size":0},{"treatment":"conta","size":0}],"label":"default rule"}]},{"trafficTypeName":"client","name":"workm_set_3","trafficAllocation":100,"trafficAllocationSeed":147392224,"seed":524417105,"status":"ACTIVE","killed":false,"defaultTreatment":"on","changeNumber":1602796638344,"algo":2,"configurations":{},"sets":["set_3"],"conditions":[{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","attribute":null},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"new_segment"},"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":0},{"treatment":"free","size":100},{"treatment":"conta","size":0}],"label":"in segment new_segment"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","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":100},{"treatment":"off","size":0},{"treatment":"free","size":0},{"treatment":"conta","size":0}],"label":"default rule"}]}],"since":1602796638344,"till":1602796638344} +{"ff":{"splits":[{"trafficTypeName":"client","name":"workm","trafficAllocation":100,"trafficAllocationSeed":147392224,"seed":524417105,"status":"ACTIVE","killed":false,"defaultTreatment":"on","changeNumber":1602796638344,"algo":2,"configurations":{},"sets":["set_1","set_2"],"conditions":[{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","attribute":null},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"new_segment"},"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":0},{"treatment":"free","size":100},{"treatment":"conta","size":0}],"label":"in segment new_segment"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","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":100},{"treatment":"off","size":0},{"treatment":"free","size":0},{"treatment":"conta","size":0}],"label":"default rule"}]},{"trafficTypeName":"client","name":"workm_set_3","trafficAllocation":100,"trafficAllocationSeed":147392224,"seed":524417105,"status":"ACTIVE","killed":false,"defaultTreatment":"on","changeNumber":1602796638344,"algo":2,"configurations":{},"sets":["set_3"],"conditions":[{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","attribute":null},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"new_segment"},"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":0},{"treatment":"free","size":100},{"treatment":"conta","size":0}],"label":"in segment new_segment"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","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":100},{"treatment":"off","size":0},{"treatment":"free","size":0},{"treatment":"conta","size":0}],"label":"default rule"}]}],"since":1602796638344,"till":1602796638344},"rbs":{"d":[],"s":1602796638341,"t":1602796638341}} diff --git a/src/androidTest/assets/split_changes_imp_toggle.json b/src/androidTest/assets/split_changes_imp_toggle.json index 2d4682675..c400fcf60 100644 --- a/src/androidTest/assets/split_changes_imp_toggle.json +++ b/src/androidTest/assets/split_changes_imp_toggle.json @@ -1,124 +1,131 @@ { - "splits": [ - { - "trafficTypeName": "user", - "name": "tracked", - "trafficAllocation": 100, - "trafficAllocationSeed": -285565213, - "seed": -1992295819, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1506703262916, - "algo": 2, - "impressionsDisabled": false, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "splits": [ + { + "trafficTypeName": "user", + "name": "tracked", + "trafficAllocation": 100, + "trafficAllocationSeed": -285565213, + "seed": -1992295819, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1506703262916, + "algo": 2, + "impressionsDisabled": false, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, { - "keySelector": { - "trafficType": "client", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "new_segment" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "free", + "size": 100 + }, + { + "treatment": "conta", + "size": 0 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 0 - }, - { - "treatment": "free", - "size": 100 + ], + "label": "in segment new_segment" + } + ] + }, + { + "trafficTypeName": "user", + "name": "not_tracked", + "trafficAllocation": 100, + "trafficAllocationSeed": -285565213, + "seed": -1992295819, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1506703262916, + "algo": 2, + "impressionsDisabled": true, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment": "conta", - "size": 0 - } - ], - "label": "in segment new_segment" - } - ] - }, - { - "trafficTypeName": "user", - "name": "not_tracked", - "trafficAllocation": 100, - "trafficAllocationSeed": -285565213, - "seed": -1992295819, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1506703262916, - "algo": 2, - "impressionsDisabled": true, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "client", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "new_segment" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "free", + "size": 100 + }, + { + "treatment": "conta", + "size": 0 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 0 - }, - { - "treatment": "free", - "size": 100 - }, - { - "treatment": "conta", - "size": 0 - } - ], - "label": "in segment new_segment" - } - ] - } - ], - "since": 1506703262916, - "till": 1506703262916 -} + ], + "label": "in segment new_segment" + } + ] + } + ], + "since": 1506703262916, + "till": 1506703262916 + }, + "rbs": { + "d": [], + "s": 1506703262911, + "t": 1506703262911 + } +} \ No newline at end of file diff --git a/src/androidTest/assets/split_changes_large_segments-0.json b/src/androidTest/assets/split_changes_large_segments-0.json index 975f211d0..4c3e613cb 100644 --- a/src/androidTest/assets/split_changes_large_segments-0.json +++ b/src/androidTest/assets/split_changes_large_segments-0.json @@ -1,4 +1,4 @@ -{ +{"ff":{ "splits": [ { "trafficTypeName": "user", @@ -46,4 +46,10 @@ ], "since": 1506703262916, "till": 1506703262916 +}, +"rbs": { + "d": [], + "s": 1506703262911, + "t": 1506703262911 +} } diff --git a/src/androidTest/assets/split_changes_semver.json b/src/androidTest/assets/split_changes_semver.json index 915178447..bb8e7f31c 100644 --- a/src/androidTest/assets/split_changes_semver.json +++ b/src/androidTest/assets/split_changes_semver.json @@ -1,431 +1,438 @@ { - "splits": [ - { - "trafficTypeName": "user", - "name": "semver_between", - "trafficAllocation": 100, - "trafficAllocationSeed": 1068038034, - "seed": -1053389887, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1675259356568, - "algo": 2, - "configurations": null, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": "version" - }, - "matcherType": "BETWEEN_SEMVER", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "dependencyMatcherData": null, - "booleanMatcherData": null, - "stringMatcherData": null, - "betweenStringMatcherData": { - "start": "1.22.9", - "end": "2.1.0" + "ff": { + "splits": [ + { + "trafficTypeName": "user", + "name": "semver_between", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": null, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "version" + }, + "matcherType": "BETWEEN_SEMVER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": null, + "betweenStringMatcherData": { + "start": "1.22.9", + "end": "2.1.0" + } } - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 + ] }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "between semver" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ + { + "treatment": "on", + "size": 100 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 0 } - ] + ], + "label": "between semver" }, - "partitions": [ - { - "treatment": "on", - "size": 0 + { + "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 + } + ] }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "default rule" - } - ] - }, - { - "trafficTypeName": "user", - "name": "semver_equalto", - "trafficAllocation": 100, - "trafficAllocationSeed": 1068038034, - "seed": -1053389887, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1675259356568, - "algo": 2, - "configurations": null, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ + { + "treatment": "on", + "size": 0 + }, { - "keySelector": { - "trafficType": "user", - "attribute": "version" - }, - "matcherType": "EQUAL_TO_SEMVER", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "dependencyMatcherData": null, - "booleanMatcherData": null, - "stringMatcherData": "1.22.9" + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "semver_equalto", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": null, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "version" + }, + "matcherType": "EQUAL_TO_SEMVER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": "1.22.9" + } + ] }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "equal to semver" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ + { + "treatment": "on", + "size": 100 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 0 } - ] + ], + "label": "equal to semver" }, - "partitions": [ - { - "treatment": "on", - "size": 0 + { + "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 + } + ] }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "default rule" - } - ] - }, - { - "trafficTypeName": "user", - "name": "semver_greater_or_equalto", - "trafficAllocation": 100, - "trafficAllocationSeed": 1068038034, - "seed": -1053389887, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1675259356568, - "algo": 2, - "configurations": null, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ + { + "treatment": "on", + "size": 0 + }, { - "keySelector": { - "trafficType": "user", - "attribute": "version" - }, - "matcherType": "GREATER_THAN_OR_EQUAL_TO_SEMVER", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "dependencyMatcherData": null, - "booleanMatcherData": null, - "stringMatcherData": "1.22.9" + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "semver_greater_or_equalto", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": null, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "version" + }, + "matcherType": "GREATER_THAN_OR_EQUAL_TO_SEMVER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": "1.22.9" + } + ] }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "greater than or equal to semver" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ + { + "treatment": "on", + "size": 100 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 0 } - ] + ], + "label": "greater than or equal to semver" }, - "partitions": [ - { - "treatment": "on", - "size": 0 + { + "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 + } + ] }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "default rule" - } - ] - }, - { - "trafficTypeName": "user", - "name": "semver_inlist", - "trafficAllocation": 100, - "trafficAllocationSeed": 1068038034, - "seed": -1053389887, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1675259356568, - "algo": 2, - "configurations": null, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ + { + "treatment": "on", + "size": 0 + }, { - "keySelector": { - "trafficType": "user", - "attribute": "version" - }, - "matcherType": "IN_LIST_SEMVER", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "1.22.9", - "2.1.0" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "dependencyMatcherData": null, - "booleanMatcherData": null, - "stringMatcherData": null, - "betweenStringMatcherData": null + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "semver_inlist", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": null, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "version" + }, + "matcherType": "IN_LIST_SEMVER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "1.22.9", + "2.1.0" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": null, + "betweenStringMatcherData": null + } + ] }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "in list semver" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 } - ] + ], + "label": "in list semver" }, - "partitions": [ - { - "treatment": "on", - "size": 0 + { + "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 + } + ] }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "default rule" - } - ] - }, - { - "trafficTypeName": "user", - "name": "semver_less_or_equalto", - "trafficAllocation": 100, - "trafficAllocationSeed": 1068038034, - "seed": -1053389887, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1675259356568, - "algo": 2, - "configurations": null, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ + { + "treatment": "on", + "size": 0 + }, { - "keySelector": { - "trafficType": "user", - "attribute": "version" - }, - "matcherType": "LESS_THAN_OR_EQUAL_TO_SEMVER", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "dependencyMatcherData": null, - "booleanMatcherData": null, - "stringMatcherData": "1.22.9" + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "semver_less_or_equalto", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": null, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "version" + }, + "matcherType": "LESS_THAN_OR_EQUAL_TO_SEMVER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": "1.22.9" + } + ] }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "less than or equal to semver" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 } - ] + ], + "label": "less than or equal to semver" }, - "partitions": [ - { - "treatment": "on", - "size": 0 + { + "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 + } + ] }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "default rule" - } - ] - } - ], - "since": -1, - "till": 1675259356568 -} + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + } + ], + "label": "default rule" + } + ] + } + ], + "since": -1, + "till": 1675259356568 + }, + "rbs": { + "d": [], + "s": -1, + "t": 1675259356561 + } +} \ No newline at end of file diff --git a/src/androidTest/assets/splitchanges_int_test.json b/src/androidTest/assets/splitchanges_int_test.json index cd02900d2..102701f09 100644 --- a/src/androidTest/assets/splitchanges_int_test.json +++ b/src/androidTest/assets/splitchanges_int_test.json @@ -1,57 +1,61 @@ -{ - "splits":[ +"ff": { + "splits": [ { - "trafficTypeName":"client", - "name":"test_feature", - "trafficAllocation":100, - "trafficAllocationSeed":-2049557248, - "seed":1188118899, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"yes", - "changeNumber":1567456937865, - "algo":2, - "configurations":{ - - }, - "conditions":[ + "trafficTypeName": "client", + "name": "test_feature", + "trafficAllocation": 100, + "trafficAllocationSeed": -2049557248, + "seed": 1188118899, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "yes", + "changeNumber": 1567456937865, + "algo": 2, + "configurations": {}, + "conditions": [ { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ { - "keySelector":{ - "trafficType":"client", - "attribute":null + "keySelector": { + "trafficType": "client", + "attribute": null }, - "matcherType":"ALL_KEYS", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null } ] }, - "partitions":[ + "partitions": [ { - "treatment":"si", - "size":0 + "treatment": "si", + "size": 0 }, { - "treatment":"no", - "size":100 + "treatment": "no", + "size": 100 } ], - "label":"default rule" + "label": "default rule" } ] } ], - "since":1567456937865, - "till":1567456937865 + "since": 1567456937865, + "till": 1567456937865 +}, +"rbs": { + "d": [], + "s": 1567456937861, + "t": 1567456937861 +} } \ No newline at end of file diff --git a/src/androidTest/assets/splitchanges_unsupported_matcher.json b/src/androidTest/assets/splitchanges_unsupported_matcher.json index 71167a2da..53379c4d1 100644 --- a/src/androidTest/assets/splitchanges_unsupported_matcher.json +++ b/src/androidTest/assets/splitchanges_unsupported_matcher.json @@ -1,89 +1,95 @@ { - "splits": [ - { - "changeNumber": 1709843458770, - "trafficTypeName": "user", - "name": "feature_flag_for_test", - "trafficAllocation": 100, - "trafficAllocationSeed": -1364119282, - "seed": -605938843, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "algo": 2, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "splits": [ + { + "changeNumber": 1709843458770, + "trafficTypeName": "user", + "name": "feature_flag_for_test", + "trafficAllocation": 100, + "trafficAllocationSeed": -1364119282, + "seed": -605938843, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "WRONG_MATCHER_TYPE", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": "123123" + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "WRONG_MATCHER_TYPE", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "dependencyMatcherData": null, - "booleanMatcherData": null, - "stringMatcherData": "123123" + "treatment": "off", + "size": 100 } - ] + ], + "label": "wrong matcher type" }, - "partitions": [ - { - "treatment": "on", - "size": 0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "sem" + }, + "matcherType": "MATCHES_STRING", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": "1.2.3" + } + ] }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "wrong matcher type" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ + { + "treatment": "on", + "size": 100 + }, { - "keySelector": { - "trafficType": "user", - "attribute": "sem" - }, - "matcherType": "MATCHES_STRING", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "dependencyMatcherData": null, - "booleanMatcherData": null, - "stringMatcherData": "1.2.3" + "treatment": "off", + "size": 0 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "sem matches 1.2.3" - } - ], - "configurations": { - }, - "sets": [] - } - ] -} + ], + "label": "sem matches 1.2.3" + } + ], + "configurations": {}, + "sets": [] + } + ] + }, + "rbs": { + "d": [], + "s": -1, + "t": 1709843458770 + } +} \ No newline at end of file diff --git a/src/test/resources/split_changes_1.json b/src/test/resources/split_changes_1.json index 7d72dbbcf..70de27a0f 100644 --- a/src/test/resources/split_changes_1.json +++ b/src/test/resources/split_changes_1.json @@ -1,2576 +1,2583 @@ { - "splits":[ - { - "trafficTypeName":"account", - "name":"FACUNDO_TEST", - "trafficAllocation":59, - "trafficAllocationSeed":-2108186082, - "seed":-1947050785, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1506703262916, - "algo":2, - "conditions":[ - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "nico_test", - "othertest" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null - } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "bla" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "ff": { + "splits": [ + { + "trafficTypeName": "account", + "name": "FACUNDO_TEST", + "trafficAllocation": 59, + "trafficAllocationSeed": -2108186082, + "seed": -1947050785, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1506703262916, + "algo": 2, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "nico_test", + "othertest" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"off", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"ALL_KEYS", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "bla" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "off", + "size": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"on", - "size":0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - }, - { - "treatment":"visa", - "size":0 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"account", - "name":"testing", - "trafficAllocation":100, - "trafficAllocationSeed":527505678, - "seed":-1716462249, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1506440189077, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"IN_SPLIT_TREATMENT", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":{ - "split":"test_definition_as_of", - "treatments":[ - "on" - ] - }, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 0 }, { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"IN_SPLIT_TREATMENT", - "negate":true, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":{ - "split":"Identify_UI", - "treatments":[ - "on" - ] - }, - "stringMatcherData":null + "treatment": "off", + "size": 100 + }, + { + "treatment": "visa", + "size": 0 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "account", + "name": "testing", + "trafficAllocation": 100, + "trafficAllocationSeed": 527505678, + "seed": -1716462249, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1506440189077, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "IN_SPLIT_TREATMENT", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": { + "split": "test_definition_as_of", + "treatments": [ + "on" + ] + }, + "stringMatcherData": null + }, + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "IN_SPLIT_TREATMENT", + "negate": true, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": { + "split": "Identify_UI", + "treatments": [ + "on" + ] + }, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":0 - } - ], - "label":"in split test_definition_as_of treatment [on] and not in split Identify_UI treatment [on]" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"IN_SPLIT_TREATMENT", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":{ - "split":"test_definition_as_of", - "treatments":[ - "off" - ] - }, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 } - ] + ], + "label": "in split test_definition_as_of treatment [on] and not in split Identify_UI treatment [on]" }, - "partitions":[ - { - "treatment":"on", - "size":0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "IN_SPLIT_TREATMENT", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": { + "split": "test_definition_as_of", + "treatments": [ + "off" + ] + }, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in split test_definition_as_of treatment [off]" - } - ] - }, - { - "trafficTypeName":"account", - "name":"testing222", - "trafficAllocation":100, - "trafficAllocationSeed":-397360967, - "seed":1058132210, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1505162627437, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "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 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 - }, - { - "treatment":"off", - "size":100 + ], + "label": "in split test_definition_as_of treatment [off]" + } + ] + }, + { + "trafficTypeName": "account", + "name": "testing222", + "trafficAllocation": 100, + "trafficAllocationSeed": -397360967, + "seed": 1058132210, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1505162627437, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"test222", - "size":0 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"account", - "name":"a_new_split_2", - "trafficAllocation":99, - "trafficAllocationSeed":-1349440646, - "seed":-1536389703, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1505161671620, - "algo":2, - "conditions":[ - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "adil", - "bb", - "bbb", - "dd3c0800-30f1-11e7-ba78-12395d4a9634", - "pato", - "tito" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null - } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"segment2" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null - } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted segment" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"test_copy" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null - } - ] - }, - "partitions":[ - { - "treatment":"off", - "size":100 - } - ], - "label":"whitelisted segment" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "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 + }, + { + "treatment": "test222", + "size": 0 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":52 - }, - { - "treatment":"off", - "size":48 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "account", + "name": "a_new_split_2", + "trafficAllocation": 99, + "trafficAllocationSeed": -1349440646, + "seed": -1536389703, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1505161671620, + "algo": 2, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "adil", + "bb", + "bbb", + "dd3c0800-30f1-11e7-ba78-12395d4a9634", + "pato", + "tito" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"testo", - "size":0 - } - ], - "label":"in segment all" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":"asda" - }, - "matcherType":"STARTS_WITH", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "ee", - "aa" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null - }, + "partitions": [ { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"IN_SEGMENT", - "negate":true, - "userDefinedSegmentMatcherData":{ - "segmentName":"segment2" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "treatment": "on", + "size": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"on", - "size":0 + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "segment2" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - }, - { - "treatment":"testo", - "size":0 - } - ], - "label":"asda starts with [ee, aa] and not in segment segment2" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":"pp" - }, - "matcherType":"PART_OF_SET", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "pato", - "adil" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 100 } - ] + ], + "label": "whitelisted segment" }, - "partitions":[ - { - "treatment":"on", - "size":100 + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "test_copy" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":0 - }, - { - "treatment":"testo", - "size":0 - } - ], - "label":"pp part of [pato, adil]" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":"eee" - }, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "1", - "2", - "trevorrr" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "off", + "size": 100 } - ] + ], + "label": "whitelisted segment" }, - "partitions":[ - { - "treatment":"on", - "size":20 - }, - { - "treatment":"off", - "size":80 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"testo", - "size":0 - } - ], - "label":"eee in list [1, 2, ...]" - } - ] - }, - { - "trafficTypeName":"account", - "name":"test_string_without_attr", - "trafficAllocation":100, - "trafficAllocationSeed":-782597068, - "seed":-1682478887, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1504805281437, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"Segment3" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 52 }, { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "something" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "treatment": "off", + "size": 48 + }, + { + "treatment": "testo", + "size": 0 } - ] + ], + "label": "in segment all" }, - "partitions":[ - { - "treatment":"on", - "size":0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": "asda" + }, + "matcherType": "STARTS_WITH", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "ee", + "aa" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }, + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": true, + "userDefinedSegmentMatcherData": { + "segmentName": "segment2" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment Segment3 and in list [something]" - } - ] - }, - { - "trafficTypeName":"user", - "name":"Test", - "trafficAllocation":100, - "trafficAllocationSeed":217539832, - "seed":52164426, - "status":"ACTIVE", - "killed":true, - "defaultTreatment":"off", - "changeNumber":1504206031141, - "algo":2, - "configurations": { - "off": "{\"f1\":\"v1\"}" - }, - "conditions":[ - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"sample-segment" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null - } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted segment" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"demo" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + }, + { + "treatment": "testo", + "size": 0 } - ] + ], + "label": "asda starts with [ee, aa] and not in segment segment2" }, - "partitions":[ - { - "treatment":"off", - "size":100 - } - ], - "label":"whitelisted segment" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"employees" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": "pp" + }, + "matcherType": "PART_OF_SET", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "pato", + "adil" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "testo", + "size": 0 } - ] + ], + "label": "pp part of [pato, adil]" }, - "partitions":[ - { - "treatment":"off", - "size":100 - } - ], - "label":"whitelisted segment" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"user", - "attribute":null - }, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"sample-segment" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": "eee" + }, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "1", + "2", + "trevorrr" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 20 }, { - "keySelector":{ - "trafficType":"user", - "attribute":"fsdfsd" - }, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "a", - "b", - "c", - "d" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "treatment": "off", + "size": 80 }, { - "keySelector":{ - "trafficType":"user", - "attribute":"asdasdasd" - }, - "matcherType":"STARTS_WITH", - "negate":true, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "asdad", - "sa", - "das" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "treatment": "testo", + "size": 0 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":10 + ], + "label": "eee in list [1, 2, ...]" + } + ] + }, + { + "trafficTypeName": "account", + "name": "test_string_without_attr", + "trafficAllocation": 100, + "trafficAllocationSeed": -782597068, + "seed": -1682478887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1504805281437, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "Segment3" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }, + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "something" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":90 - } - ], - "label":"in segment sample-segment and fsdfsd in list [a, b, ...] and asdasdasd does not start with [asdad, sa, ...]" - } - ] - }, - { - "trafficTypeName":"account", - "name":"Test_Save_1", - "trafficAllocation":100, - "trafficAllocationSeed":-257595325, - "seed":-665945237, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1503956389520, - "algo":2, - "conditions":[ - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "1", - "12", - "123", - "23" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted" + ], + "label": "in segment Segment3 and in list [something]" + } + ] + }, + { + "trafficTypeName": "user", + "name": "Test", + "trafficAllocation": 100, + "trafficAllocationSeed": 217539832, + "seed": 52164426, + "status": "ACTIVE", + "killed": true, + "defaultTreatment": "off", + "changeNumber": 1504206031141, + "algo": 2, + "configurations": { + "off": "{\"f1\":\"v1\"}" }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "asd" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "sample-segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 } - ] + ], + "label": "whitelisted segment" }, - "partitions":[ - { - "treatment":"off", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"Segment3" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "demo" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "off", + "size": 100 } - ] + ], + "label": "whitelisted segment" }, - "partitions":[ - { - "treatment":"off", - "size":100 - } - ], - "label":"whitelisted segment" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"ALL_KEYS", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "employees" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "off", + "size": 100 } - ] + ], + "label": "whitelisted segment" }, - "partitions":[ - { - "treatment":"on", - "size":0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "sample-segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }, + { + "keySelector": { + "trafficType": "user", + "attribute": "fsdfsd" + }, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "a", + "b", + "c", + "d" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }, + { + "keySelector": { + "trafficType": "user", + "attribute": "asdasdasd" + }, + "matcherType": "STARTS_WITH", + "negate": true, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "asdad", + "sa", + "das" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 + "partitions": [ + { + "treatment": "on", + "size": 10 + }, + { + "treatment": "off", + "size": 90 + } + ], + "label": "in segment sample-segment and fsdfsd in list [a, b, ...] and asdasdasd does not start with [asdad, sa, ...]" + } + ] + }, + { + "trafficTypeName": "account", + "name": "Test_Save_1", + "trafficAllocation": 100, + "trafficAllocationSeed": -257595325, + "seed": -665945237, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1503956389520, + "algo": 2, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "1", + "12", + "123", + "23" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"v1", - "size":0 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"account", - "name":"TEST", - "trafficAllocation":100, - "trafficAllocationSeed":-673356676, - "seed":-511119211, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1503942404754, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "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": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"on", - "size":100 + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "asd" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":0 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"benchmark_jw_1", - "trafficAllocation":100, - "trafficAllocationSeed":987354894, - "seed":1292874260, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1503356075822, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"user", - "attribute":"atrib" - }, - "matcherType":"BETWEEN", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":{ - "dataType":"NUMBER", - "start":1474990940, - "end":1474990949 - }, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "off", + "size": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"on", - "size":95 + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "Segment3" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":5 - } - ], - "label":"atrib between 1474990940 and 1474990949" - }, - { - "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": "off", + "size": 100 } - ] + ], + "label": "whitelisted segment" }, - "partitions":[ - { - "treatment":"on", - "size":90 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":10 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"nico_tests", - "trafficAllocation":100, - "trafficAllocationSeed":1409699192, - "seed":-1997241870, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1501791316810, - "algo":2, - "conditions":[ - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "nico_test_browser__key" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + }, + { + "treatment": "v1", + "size": 0 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"user", - "attribute":null - }, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"employees" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "account", + "name": "TEST", + "trafficAllocation": 100, + "trafficAllocationSeed": -673356676, + "seed": -511119211, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1503942404754, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "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": 100 + }, + { + "treatment": "off", + "size": 0 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "benchmark_jw_1", + "trafficAllocation": 100, + "trafficAllocationSeed": 987354894, + "seed": 1292874260, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1503356075822, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "atrib" + }, + "matcherType": "BETWEEN", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": { + "dataType": "NUMBER", + "start": 1474990940, + "end": 1474990949 + }, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":0 - } - ], - "label":"in segment employees" - } - ] - }, - { - "trafficTypeName":"account", - "name":"testo2222", - "trafficAllocation":100, - "trafficAllocationSeed":1164474086, - "seed":1270508512, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1501012403336, - "algo":2, - "conditions":[ - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "aasd" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 95 + }, + { + "treatment": "off", + "size": 5 } - ] + ], + "label": "atrib between 1474990940 and 1474990949" }, - "partitions":[ - { - "treatment":"off", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"segment2" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + { + "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": 90 + }, + { + "treatment": "off", + "size": 10 } - ] - }, - "partitions":[ - { - "treatment":"off", - "size":100 - } - ], - "label":"whitelisted segment" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "ddddd" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "nico_tests", + "trafficAllocation": 100, + "trafficAllocationSeed": 1409699192, + "seed": -1997241870, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1501791316810, + "algo": 2, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "nico_test_browser__key" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"Segment3" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "employees" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted segment" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "ppp", - "ppppp" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + ], + "label": "in segment employees" + } + ] + }, + { + "trafficTypeName": "account", + "name": "testo2222", + "trafficAllocation": 100, + "trafficAllocationSeed": 1164474086, + "seed": 1270508512, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1501012403336, + "algo": 2, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "aasd" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "off", + "size": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"pesto", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"test_copy" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "segment2" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "off", + "size": 100 } - ] + ], + "label": "whitelisted segment" }, - "partitions":[ - { - "treatment":"pesto", - "size":100 - } - ], - "label":"whitelisted segment" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"ALL_KEYS", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "ddddd" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"off", - "size":0 - }, - { - "treatment":"on", - "size":100 - }, - { - "treatment":"pesto", - "size":0 + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "Segment3" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"arse", - "size":0 - }, - { - "treatment":"zzzzick", - "size":0 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"Tagging", - "trafficAllocation":100, - "trafficAllocationSeed":1910132597, - "seed":-311493896, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1500590774768, - "algo":2, - "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": 100 } - ] + ], + "label": "whitelisted segment" }, - "partitions":[ - { - "treatment":"on", - "size":50 + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "ppp", + "ppppp" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":50 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"account", - "name":"Welcome_Page_UI", - "trafficAllocation":100, - "trafficAllocationSeed":1848523960, - "seed":1608586361, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1500577256901, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"ALL_KEYS", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "pesto", + "size": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"on", - "size":0 + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "test_copy" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"test", - "name":"pato_test_3", - "trafficAllocation":100, - "trafficAllocationSeed":458647735, - "seed":95677506, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1500510847849, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"test", - "attribute":null - }, - "matcherType":"ALL_KEYS", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "pesto", + "size": 100 } - ] + ], + "label": "whitelisted segment" }, - "partitions":[ - { - "treatment":"on", - "size":0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"account", - "name":"testo23", - "trafficAllocation":100, - "trafficAllocationSeed":-689658216, - "seed":1711156051, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1500064145947, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":"sdsd" - }, - "matcherType":"EQUAL_TO_BOOLEAN", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":true, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "pesto", + "size": 0 + }, + { + "treatment": "arse", + "size": 0 + }, + { + "treatment": "zzzzick", + "size": 0 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "Tagging", + "trafficAllocation": 100, + "trafficAllocationSeed": 1910132597, + "seed": -311493896, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1500590774768, + "algo": 2, + "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 + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"sdsd is true" - } - ] - }, - { - "trafficTypeName":"account", - "name":"testo909090", - "trafficAllocation":100, - "trafficAllocationSeed":-1196467266, - "seed":-1998101827, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1500039488369, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":"a" - }, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "a" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 50 }, { - "keySelector":{ - "trafficType":"account", - "attribute":"v" - }, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "a" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "treatment": "off", + "size": 50 + } + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "account", + "name": "Welcome_Page_UI", + "trafficAllocation": 100, + "trafficAllocationSeed": 1848523960, + "seed": 1608586361, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1500577256901, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "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 }, { - "keySelector":{ - "trafficType":"account", - "attribute":"asdadas" - }, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "a", - "b", - "c", - "d" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "treatment": "off", + "size": 100 + } + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "test", + "name": "pato_test_3", + "trafficAllocation": 100, + "trafficAllocationSeed": 458647735, + "seed": 95677506, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1500510847849, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "test", + "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 }, { - "keySelector":{ - "trafficType":"account", - "attribute":"sds" - }, - "matcherType":"CONTAINS_STRING", - "negate":true, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "a", - "c", - "d" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "treatment": "off", + "size": 100 + } + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "account", + "name": "testo23", + "trafficAllocation": 100, + "trafficAllocationSeed": -689658216, + "seed": 1711156051, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1500064145947, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": "sdsd" + }, + "matcherType": "EQUAL_TO_BOOLEAN", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": true, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 }, { - "keySelector":{ - "trafficType":"account", - "attribute":"xcvxv" - }, - "matcherType":"EQUAL_TO", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":{ - "dataType":"NUMBER", - "value":122 - }, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "treatment": "off", + "size": 100 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "sdsd is true" + } + ] + }, + { + "trafficTypeName": "account", + "name": "testo909090", + "trafficAllocation": 100, + "trafficAllocationSeed": -1196467266, + "seed": -1998101827, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1500039488369, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": "a" + }, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "a" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }, + { + "keySelector": { + "trafficType": "account", + "attribute": "v" + }, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "a" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }, + { + "keySelector": { + "trafficType": "account", + "attribute": "asdadas" + }, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "a", + "b", + "c", + "d" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }, + { + "keySelector": { + "trafficType": "account", + "attribute": "sds" + }, + "matcherType": "CONTAINS_STRING", + "negate": true, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "a", + "c", + "d" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }, + { + "keySelector": { + "trafficType": "account", + "attribute": "xcvxv" + }, + "matcherType": "EQUAL_TO", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": { + "dataType": "NUMBER", + "value": 122 + }, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"a in list [a] and v in list [a] and asdadas in list [a, b, ...] and sds does not contain [a, c, ...] and xcvxv = 122" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "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": "a in list [a] and v in list [a] and asdadas in list [a, b, ...] and sds does not contain [a, c, ...] and xcvxv = 122" }, - "partitions":[ - { - "treatment":"on", - "size":100 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":0 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"account", - "name":"testo22", - "trafficAllocation":100, - "trafficAllocationSeed":1223277820, - "seed":-1152948537, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1499721434259, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "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": 100 + }, + { + "treatment": "off", + "size": 0 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "account", + "name": "testo22", + "trafficAllocation": 100, + "trafficAllocationSeed": 1223277820, + "seed": -1152948537, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1499721434259, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"test-net", - "trafficAllocation":100, - "trafficAllocationSeed":-2038196969, - "seed":-862203077, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1499718635999, - "algo":2, - "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 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "test-net", + "trafficAllocation": 100, + "trafficAllocationSeed": -2038196969, + "seed": -862203077, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1499718635999, + "algo": 2, + "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 + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"account", - "name":"test_dep_2", - "trafficAllocation":100, - "trafficAllocationSeed":-806171485, - "seed":922684950, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1499707910800, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"IN_SPLIT_TREATMENT", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":{ - "split":"Identify_UI", - "treatments":[ - "on" - ] - }, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "account", + "name": "test_dep_2", + "trafficAllocation": 100, + "trafficAllocationSeed": -806171485, + "seed": 922684950, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1499707910800, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "IN_SPLIT_TREATMENT", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": { + "split": "Identify_UI", + "treatments": [ + "on" + ] + }, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in split Identify_UI treatment [on]" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"IN_SPLIT_TREATMENT", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":{ - "split":"Definition_As_Of_Clickable_UI", - "treatments":[ - "off" - ] - }, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 } - ] + ], + "label": "in split Identify_UI treatment [on]" }, - "partitions":[ - { - "treatment":"on", - "size":50 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "IN_SPLIT_TREATMENT", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": { + "split": "Definition_As_Of_Clickable_UI", + "treatments": [ + "off" + ] + }, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":50 - } - ], - "label":"in split Definition_As_Of_Clickable_UI treatment [off]" - } - ] - }, - { - "trafficTypeName":"account", - "name":"Definition_As_Of_Clickable_UI", - "trafficAllocation":100, - "trafficAllocationSeed":-198035199, - "seed":-151947071, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1498168847351, - "algo":2, - "conditions":[ - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "tito", - "trevor" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 50 + }, + { + "treatment": "off", + "size": 50 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"ALL_KEYS", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + ], + "label": "in split Definition_As_Of_Clickable_UI treatment [off]" + } + ] + }, + { + "trafficTypeName": "account", + "name": "Definition_As_Of_Clickable_UI", + "trafficAllocation": 100, + "trafficAllocationSeed": -198035199, + "seed": -151947071, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1498168847351, + "algo": 2, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "tito", + "trevor" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"on", - "size":0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"account", - "name":"Identify_UI", - "trafficAllocation":100, - "trafficAllocationSeed":-139516103, - "seed":1543172523, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1498078888450, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "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 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "account", + "name": "Identify_UI", + "trafficAllocation": 100, + "trafficAllocationSeed": -139516103, + "seed": 1543172523, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1498078888450, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":0 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"account", - "name":"test_definition_as_of", - "trafficAllocation":100, - "trafficAllocationSeed":1025823325, - "seed":-554248124, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1497289730024, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"ALL_KEYS", - "negatee":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "account", + "name": "test_definition_as_of", + "trafficAllocation": 100, + "trafficAllocationSeed": 1025823325, + "seed": -554248124, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1497289730024, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negatee": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"Test-jw-go", - "trafficAllocation":100, - "trafficAllocationSeed":768122971, - "seed":1539205707, - "status":"ACTIVE", - "defaultTreatment":"off", - "changeNumber":1496339112852, - "algo":2, - "conditions":[ - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "test1" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "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 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "Test-jw-go", + "trafficAllocation": 100, + "trafficAllocationSeed": 768122971, + "seed": 1539205707, + "status": "ACTIVE", + "defaultTreatment": "off", + "changeNumber": 1496339112852, + "algo": 2, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "test1" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"on", - "size":0 + { + "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 + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"benchmark_jw_7", - "trafficAllocation":100, - "trafficAllocationSeed":-1340337178, - "seed":-1091938685, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1494593464885, - "algo":2, - "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 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "benchmark_jw_7", + "trafficAllocation": 100, + "trafficAllocationSeed": -1340337178, + "seed": -1091938685, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1494593464885, + "algo": 2, + "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 + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"benchmark_jw_6", - "trafficAllocation":100, - "trafficAllocationSeed":-1202331834, - "seed":-48445256, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1494593448028, - "algo":2, - "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 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "benchmark_jw_6", + "trafficAllocation": 100, + "trafficAllocationSeed": -1202331834, + "seed": -48445256, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1494593448028, + "algo": 2, + "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 + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"benchmark_jw_5", - "trafficAllocation":100, - "trafficAllocationSeed":2119994290, - "seed":-227092192, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1494593428034, - "algo":2, - "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 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "benchmark_jw_5", + "trafficAllocation": 100, + "trafficAllocationSeed": 2119994290, + "seed": -227092192, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1494593428034, + "algo": 2, + "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 + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"benchmark_jw_4", - "trafficAllocation":100, - "trafficAllocationSeed":1066635158, - "seed":-850704283, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1494593412226, - "algo":2, - "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 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "benchmark_jw_4", + "trafficAllocation": 100, + "trafficAllocationSeed": 1066635158, + "seed": -850704283, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1494593412226, + "algo": 2, + "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 + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"benchmark_jw_3", - "trafficAllocation":100, - "trafficAllocationSeed":1252392550, - "seed":971538037, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1494593352077, - "algo":2, - "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 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "benchmark_jw_3", + "trafficAllocation": 100, + "trafficAllocationSeed": 1252392550, + "seed": 971538037, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1494593352077, + "algo": 2, + "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 + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"benchmark_jw_2", - "trafficAllocation":100, - "trafficAllocationSeed":-285565213, - "seed":-1992295819, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1494593336752, - "algo":2, - "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 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "benchmark_jw_2", + "trafficAllocation": 100, + "trafficAllocationSeed": -285565213, + "seed": -1992295819, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1494593336752, + "algo": 2, + "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 + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"account", - "name":"ls_split", - "trafficAllocation":100, - "trafficAllocationSeed":-285565213, - "seed":-1992295819, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1506703262916, - "algo":2, - "conditions":[ - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"IN_LARGE_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"segment1" - }, - "userDefinedLargeSegmentMatcherData":{ - "largeSegmentName":"segment1" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted segment" - } - ] - }, - { - "trafficTypeName":"user", - "name":"broken_split", - "trafficAllocation":100, - "trafficAllocationSeed":-285565213, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1494593336752 - } - ], - "since":-1, - "till":1506703262916 -} + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "account", + "name": "ls_split", + "trafficAllocation": 100, + "trafficAllocationSeed": -285565213, + "seed": -1992295819, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1506703262916, + "algo": 2, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_LARGE_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "segment1" + }, + "userDefinedLargeSegmentMatcherData": { + "largeSegmentName": "segment1" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + } + ], + "label": "whitelisted segment" + } + ] + }, + { + "trafficTypeName": "user", + "name": "broken_split", + "trafficAllocation": 100, + "trafficAllocationSeed": -285565213, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1494593336752 + } + ], + "since": -1, + "till": 1506703262916 + }, + "rbs": { + "d": [], + "s": -1, + "t": 1506703261916 + } +} \ No newline at end of file diff --git a/src/test/resources/split_changes_1_updated.json b/src/test/resources/split_changes_1_updated.json new file mode 100644 index 000000000..e937cc953 --- /dev/null +++ b/src/test/resources/split_changes_1_updated.json @@ -0,0 +1,201 @@ +{ + "ff": { + "splits":[ + { + "trafficTypeName":"account", + "name":"FACUNDO_TEST", + "trafficAllocation":59, + "trafficAllocationSeed":-2108186082, + "seed":-1947050785, + "status":"ACTIVE", + "killed":false, + "defaultTreatment":"off", + "changeNumber":1506703262916, + "algo":2, + "conditions":[ + { + "conditionType":"WHITELIST", + "matcherGroup":{ + "combiner":"AND", + "matchers":[ + { + "keySelector":null, + "matcherType":"WHITELIST", + "negate":false, + "userDefinedSegmentMatcherData":null, + "whitelistMatcherData":{ + "whitelist":[ + "nico_test", + "othertest" + ] + }, + "unaryNumericMatcherData":null, + "betweenMatcherData":null, + "booleanMatcherData":null, + "dependencyMatcherData":null, + "stringMatcherData":null + } + ] + }, + "partitions":[ + { + "treatment":"on", + "size":100 + } + ], + "label":"whitelisted" + }, + { + "conditionType":"WHITELIST", + "matcherGroup":{ + "combiner":"AND", + "matchers":[ + { + "keySelector":null, + "matcherType":"WHITELIST", + "negate":false, + "userDefinedSegmentMatcherData":null, + "whitelistMatcherData":{ + "whitelist":[ + "bla" + ] + }, + "unaryNumericMatcherData":null, + "betweenMatcherData":null, + "booleanMatcherData":null, + "dependencyMatcherData":null, + "stringMatcherData":null + } + ] + }, + "partitions":[ + { + "treatment":"off", + "size":100 + } + ], + "label":"whitelisted" + }, + { + "conditionType":"ROLLOUT", + "matcherGroup":{ + "combiner":"AND", + "matchers":[ + { + "keySelector":{ + "trafficType":"account", + "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 + }, + { + "treatment":"visa", + "size":0 + } + ], + "label":"in segment all" + } + ] + }, + { + "trafficTypeName":"account", + "name":"testing", + "trafficAllocation":100, + "trafficAllocationSeed":527505678, + "seed":-1716462249, + "status":"ACTIVE", + "killed":false, + "defaultTreatment":"off", + "changeNumber":1506440189077, + "algo":2, + "conditions":[ + { + "conditionType":"ROLLOUT", + "matcherGroup":{ + "combiner":"AND", + "matchers":[ + { + "keySelector":{ + "trafficType":"account", + "attribute":null + }, + "matcherType":"IN_SPLIT_TREATMENT", + "negate":false, + "userDefinedSegmentMatcherData":null, + "whitelistMatcherData":null, + "unaryNumericMatcherData":null, + "betweenMatcherData":null, + "booleanMatcherData":null, + "dependencyMatcherData":{ + "split":"test_definition_as_of", + "treatments":[ + "on" + ] + }, + "stringMatcherData":null + }, + { + "keySelector":{ + "trafficType":"account", + "attribute":null + }, + "matcherType":"IN_SPLIT_TREATMENT", + "negate":true, + "userDefinedSegmentMatcherData":null, + "whitelistMatcherData":null, + "unaryNumericMatcherData":null, + "betweenMatcherData":null, + "booleanMatcherData":null, + "dependencyMatcherData":{ + "split":"Identify_UI", + "treatments":[ + "on" + ] + }, + "stringMatcherData":null + } + ] + }, + "partitions":[ + { + "treatment":"on", + "size":100 + }, + { + "treatment":"off", + "size":0 + } + ], + "label":"in split test_definition_as_of treatment [on] and not in split Identify_UI treatment [on]" + } + ] + } + ], + "since":-1, + "till":1506703262916 + }, + "rbs": { + "d": [], + "s": -1, + "t": 1506703262916 + } +} From d71490378c23d8b30ee0ca7d09c97b296f806793 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Wed, 5 Mar 2025 11:02:26 -0300 Subject: [PATCH 23/26] UT migrated --- .../assets/splitchanges_int_test.json | 117 +++++++++--------- .../client/network/HttpClientTest.java | 3 +- .../io/split/android/helpers/FileHelper.java | 13 +- 3 files changed, 68 insertions(+), 65 deletions(-) diff --git a/src/androidTest/assets/splitchanges_int_test.json b/src/androidTest/assets/splitchanges_int_test.json index 102701f09..2073118b1 100644 --- a/src/androidTest/assets/splitchanges_int_test.json +++ b/src/androidTest/assets/splitchanges_int_test.json @@ -1,61 +1,62 @@ -"ff": { - "splits": [ - { - "trafficTypeName": "client", - "name": "test_feature", - "trafficAllocation": 100, - "trafficAllocationSeed": -2049557248, - "seed": 1188118899, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "yes", - "changeNumber": 1567456937865, - "algo": 2, - "configurations": {}, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ +{ + "ff": { + "splits": [ + { + "trafficTypeName": "client", + "name": "test_feature", + "trafficAllocation": 100, + "trafficAllocationSeed": -2049557248, + "seed": 1188118899, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "yes", + "changeNumber": 1567456937865, + "algo": 2, + "configurations": {}, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "si", + "size": 0 + }, { - "keySelector": { - "trafficType": "client", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "no", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "si", - "size": 0 - }, - { - "treatment": "no", - "size": 100 - } - ], - "label": "default rule" - } - ] - } - ], - "since": 1567456937865, - "till": 1567456937865 -}, -"rbs": { - "d": [], - "s": 1567456937861, - "t": 1567456937861 -} + ], + "label": "default rule" + } + ] + } + ], + "since": 1567456937865, + "till": 1567456937865 + }, + "rbs": { + "d": [], + "s": 1567456937861, + "t": 1567456937861 + } } \ No newline at end of file diff --git a/src/test/java/io/split/android/client/network/HttpClientTest.java b/src/test/java/io/split/android/client/network/HttpClientTest.java index b036ec5b8..c78b52696 100644 --- a/src/test/java/io/split/android/client/network/HttpClientTest.java +++ b/src/test/java/io/split/android/client/network/HttpClientTest.java @@ -36,6 +36,7 @@ import io.split.android.client.dtos.AllSegmentsChange; import io.split.android.client.dtos.Event; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.dtos.TestImpressions; import io.split.android.client.utils.Json; import io.split.android.helpers.FileHelper; @@ -149,7 +150,7 @@ public void severalRequest() throws Exception { Assert.assertTrue(mySegments.contains("groupb")); // Assert split changes - SplitChange splitChange = Json.fromJson(splitChangeResp.getData(), SplitChange.class); + SplitChange splitChange = Json.fromJson(splitChangeResp.getData(), TargetingRulesChange.class).getFeatureFlagsChange(); Assert.assertEquals(200, splitChangeResp.getHttpStatus()); assertTrue(splitChangeResp.isSuccess()); Assert.assertEquals(-1, splitChange.since); diff --git a/src/test/java/io/split/android/helpers/FileHelper.java b/src/test/java/io/split/android/helpers/FileHelper.java index 15adcd6fd..353e05e0a 100644 --- a/src/test/java/io/split/android/helpers/FileHelper.java +++ b/src/test/java/io/split/android/helpers/FileHelper.java @@ -11,26 +11,27 @@ import io.split.android.client.dtos.Split; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.utils.Json; public class FileHelper { - public List loadAndParseSplitChangeFile (String name) { + public List loadAndParseSplitChangeFile(String name) { try { String content = loadFileContent(name); - SplitChange change = Json.fromJson(content, SplitChange.class); - return change.splits; + TargetingRulesChange change = Json.fromJson(content, TargetingRulesChange.class); + return change.getFeatureFlagsChange().splits; } catch (Exception e) { System.out.println("loadAndParseSplitChangeFile: Failed load file content" + e.getLocalizedMessage()); } return null; } - public SplitChange loadSplitChangeFromFile (String name) { + public SplitChange loadSplitChangeFromFile(String name) { try { String content = loadFileContent(name); - SplitChange change = Json.fromJson(content, SplitChange.class); - return change; + TargetingRulesChange change = Json.fromJson(content, TargetingRulesChange.class); + return change.getFeatureFlagsChange(); } catch (Exception e) { } return null; From 9f29fb3e0152efe6e0a56795396f1c689bb3e543 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Wed, 5 Mar 2025 13:53:17 -0300 Subject: [PATCH 24/26] WIP tests migration --- .../assets/attributes_test_split_change.json | 335 +- src/androidTest/assets/bucket_split_test.json | 879 +-- src/androidTest/assets/simple_split.json | 205 +- src/androidTest/assets/split_changes_1.json | 4803 +++++++++-------- .../split_changes_large_segments-0.json | 105 +- .../java/helper/IntegrationHelper.java | 10 +- .../integration/FlagsSpecInRequestTest.java | 3 +- .../tests/integration/IntegrationTest.java | 2 +- .../integration/MySegmentUpdatedTest.java | 6 +- .../MySegmentsServerErrorTest.java | 7 +- .../SplitChangesCdnBypassTest.java | 4 +- .../SplitChangesServerErrorTest.java | 3 +- .../tests/integration/SplitChangesTest.java | 4 +- .../SplitsTwoDifferentApiKeyTest.java | 4 +- .../java/tests/integration/TrackTest.java | 2 +- .../attributes/AttributesIntegrationTest.java | 2 +- .../encryption/EncryptionTest.java | 2 +- .../largesegments/LargeSegmentTestHelper.java | 2 +- .../LargeSegmentsStreamingTest.java | 2 +- .../RolloutCacheManagerIntegrationTest.java | 2 +- .../sets/FlagSetsEvaluationTest.java | 2 +- .../sets/FlagSetsMultipleFactoryTest.java | 2 +- .../integration/sets/FlagSetsPollingTest.java | 2 +- .../shared/SharedClientsIntegrationTest.java | 2 +- .../streaming/ImpressionsCountTest.java | 12 +- .../streaming/SdkUpdateStreamingTest.java | 4 +- .../streaming/SplitsKillProcessTest.java | 7 +- .../streaming/SplitsSyncProcessTest.java | 4 +- .../telemetry/TelemetryIntegrationTest.java | 2 +- .../tests/service/SdkUpdatePollingTest.java | 7 +- .../service/splits/SplitsSyncHelper.java | 3 +- 31 files changed, 3234 insertions(+), 3195 deletions(-) diff --git a/src/androidTest/assets/attributes_test_split_change.json b/src/androidTest/assets/attributes_test_split_change.json index 980b0a0fb..69a7aa177 100644 --- a/src/androidTest/assets/attributes_test_split_change.json +++ b/src/androidTest/assets/attributes_test_split_change.json @@ -1,182 +1,187 @@ { - "splits":[ + "ff": { + "splits": [ { - "trafficTypeName":"client", - "name":"workm", - "trafficAllocation":100, - "trafficAllocationSeed":147392224, - "seed":524417105, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"on", - "changeNumber":1602796638344, - "algo":2, - "configurations":{ - - }, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"client", - "attribute":"num_value" - }, - "matcherType":"EQUAL_TO", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":{ - "dataType":"NUMBER", - "value":10 - }, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null - } - ] - }, - "partitions":[ - { - "treatment":"on_num_10", - "size":100 + "trafficTypeName": "client", + "name": "workm", + "trafficAllocation": 100, + "trafficAllocationSeed": 147392224, + "seed": 524417105, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1602796638344, + "algo": 2, + "configurations": { + }, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": "num_value" }, - { - "treatment":"off", - "size":0 - } - ], - "label":"rule 1" - } - ] - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"client", - "attribute":"str_value" + "matcherType": "EQUAL_TO", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": { + "dataType": "NUMBER", + "value": 10 }, - "matcherType":"MATCHES_STRING", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":"yes" - } - ] - }, - "partitions":[ - { - "treatment":"on_str_yes", - "size":100 + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":0 - } - ], - "label":"rule 2" + "partitions": [ + { + "treatment": "on_num_10", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "rule 1" + } + ] }, { - "trafficTypeName":"client", - "name":"workm1", - "trafficAllocation":100, - "trafficAllocationSeed":147392224, - "seed":524417105, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"on", - "changeNumber":1602796638344, - "algo":2, - "configurations":{ - - }, - "conditions":[ + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"client", - "attribute":"num_value_a" - }, - "matcherType":"EQUAL_TO", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":{ - "dataType":"NUMBER", - "value":20 - }, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null - } - ] - }, - "partitions":[ - { - "treatment":"on_num_20", - "size":100 - }, - { - "treatment":"off", - "size":0 - } - ], - "label":"rule 3" + "keySelector": { + "trafficType": "client", + "attribute": "str_value" + }, + "matcherType": "MATCHES_STRING", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": "yes" } - ] + ] + }, + "partitions": [ + { + "treatment": "on_str_yes", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "rule 2" }, { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"client", - "attribute":"str_value_a" + "trafficTypeName": "client", + "name": "workm1", + "trafficAllocation": 100, + "trafficAllocationSeed": 147392224, + "seed": 524417105, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1602796638344, + "algo": 2, + "configurations": { + }, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": "num_value_a" }, - "matcherType":"MATCHES_STRING", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":"no" - } - ] - }, - "partitions":[ - { - "treatment":"on_str_no", - "size":100 + "matcherType": "EQUAL_TO", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": { + "dataType": "NUMBER", + "value": 20 + }, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, + "partitions": [ + { + "treatment": "on_num_20", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "rule 3" + } + ] + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ { - "treatment":"off", - "size":0 + "keySelector": { + "trafficType": "client", + "attribute": "str_value_a" + }, + "matcherType": "MATCHES_STRING", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": "no" } - ], - "label":"rule 3" + ] + }, + "partitions": [ + { + "treatment": "on_str_no", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "rule 3" } - ], - "since":1602796638344, - "till":1602796638344 + ], + "since": 1602796638344, + "till": 1602796638344 + }, + "rbs": { + "d": [], + "s": 1602796638341, + "t": 1602796638341 + } } \ No newline at end of file diff --git a/src/androidTest/assets/bucket_split_test.json b/src/androidTest/assets/bucket_split_test.json index 1a9d3e212..f5608b9b7 100644 --- a/src/androidTest/assets/bucket_split_test.json +++ b/src/androidTest/assets/bucket_split_test.json @@ -1,439 +1,446 @@ { - "splits": [ - { - "name": "bucket_test", - "changeNumber": 1506703262916, - "trafficAllocationSeed": -1277047201, - "seed": 1844331483, - "configurations": {}, - "conditions": [ - { - "conditionType": "rollout", - "label": "default rule", - "matcherGroup": { - "matchers": [ - { - "matcherType": "ALL_KEYS", - "keySelector": { - "trafficType": "account" - }, - "negate": false + "ff": { + "splits": [ + { + "name": "bucket_test", + "changeNumber": 1506703262916, + "trafficAllocationSeed": -1277047201, + "seed": 1844331483, + "configurations": {}, + "conditions": [ + { + "conditionType": "rollout", + "label": "default rule", + "matcherGroup": { + "matchers": [ + { + "matcherType": "ALL_KEYS", + "keySelector": { + "trafficType": "account" + }, + "negate": false + } + ], + "combiner": "AND" + }, + "partitions": [ + { + "size": 1, + "treatment": "V1" + }, + { + "size": 1, + "treatment": "V2" + }, + { + "size": 1, + "treatment": "V3" + }, + { + "size": 1, + "treatment": "V4" + }, + { + "size": 1, + "treatment": "V5" + }, + { + "size": 1, + "treatment": "V6" + }, + { + "size": 1, + "treatment": "V7" + }, + { + "size": 1, + "treatment": "V8" + }, + { + "size": 1, + "treatment": "V9" + }, + { + "size": 1, + "treatment": "V10" + }, + { + "size": 1, + "treatment": "V11" + }, + { + "size": 1, + "treatment": "V12" + }, + { + "size": 1, + "treatment": "V13" + }, + { + "size": 1, + "treatment": "V14" + }, + { + "size": 1, + "treatment": "V15" + }, + { + "size": 1, + "treatment": "V16" + }, + { + "size": 1, + "treatment": "V17" + }, + { + "size": 1, + "treatment": "V18" + }, + { + "size": 1, + "treatment": "V19" + }, + { + "size": 1, + "treatment": "V20" + }, + { + "size": 1, + "treatment": "V21" + }, + { + "size": 1, + "treatment": "V22" + }, + { + "size": 1, + "treatment": "V23" + }, + { + "size": 1, + "treatment": "V24" + }, + { + "size": 1, + "treatment": "V25" + }, + { + "size": 1, + "treatment": "V26" + }, + { + "size": 1, + "treatment": "V27" + }, + { + "size": 1, + "treatment": "V28" + }, + { + "size": 1, + "treatment": "V29" + }, + { + "size": 1, + "treatment": "V30" + }, + { + "size": 1, + "treatment": "V31" + }, + { + "size": 1, + "treatment": "V32" + }, + { + "size": 1, + "treatment": "V33" + }, + { + "size": 1, + "treatment": "V34" + }, + { + "size": 1, + "treatment": "V35" + }, + { + "size": 1, + "treatment": "V36" + }, + { + "size": 1, + "treatment": "V37" + }, + { + "size": 1, + "treatment": "V38" + }, + { + "size": 1, + "treatment": "V39" + }, + { + "size": 1, + "treatment": "V40" + }, + { + "size": 1, + "treatment": "V41" + }, + { + "size": 1, + "treatment": "V42" + }, + { + "size": 1, + "treatment": "V43" + }, + { + "size": 1, + "treatment": "V44" + }, + { + "size": 1, + "treatment": "V45" + }, + { + "size": 1, + "treatment": "V46" + }, + { + "size": 1, + "treatment": "V47" + }, + { + "size": 1, + "treatment": "V48" + }, + { + "size": 1, + "treatment": "V49" + }, + { + "size": 1, + "treatment": "V50" + }, + { + "size": 1, + "treatment": "V51" + }, + { + "size": 1, + "treatment": "V52" + }, + { + "size": 1, + "treatment": "V53" + }, + { + "size": 1, + "treatment": "V54" + }, + { + "size": 1, + "treatment": "V55" + }, + { + "size": 1, + "treatment": "V56" + }, + { + "size": 1, + "treatment": "V57" + }, + { + "size": 1, + "treatment": "V58" + }, + { + "size": 1, + "treatment": "V59" + }, + { + "size": 1, + "treatment": "V60" + }, + { + "size": 1, + "treatment": "V61" + }, + { + "size": 1, + "treatment": "V62" + }, + { + "size": 1, + "treatment": "V63" + }, + { + "size": 1, + "treatment": "V64" + }, + { + "size": 1, + "treatment": "V65" + }, + { + "size": 1, + "treatment": "V66" + }, + { + "size": 1, + "treatment": "V67" + }, + { + "size": 1, + "treatment": "V68" + }, + { + "size": 1, + "treatment": "V69" + }, + { + "size": 1, + "treatment": "V70" + }, + { + "size": 1, + "treatment": "V71" + }, + { + "size": 1, + "treatment": "V72" + }, + { + "size": 1, + "treatment": "V73" + }, + { + "size": 1, + "treatment": "V74" + }, + { + "size": 1, + "treatment": "V75" + }, + { + "size": 1, + "treatment": "V76" + }, + { + "size": 1, + "treatment": "V77" + }, + { + "size": 1, + "treatment": "V78" + }, + { + "size": 1, + "treatment": "V79" + }, + { + "size": 1, + "treatment": "V80" + }, + { + "size": 1, + "treatment": "V81" + }, + { + "size": 1, + "treatment": "V82" + }, + { + "size": 1, + "treatment": "V83" + }, + { + "size": 1, + "treatment": "V84" + }, + { + "size": 1, + "treatment": "V85" + }, + { + "size": 1, + "treatment": "V86" + }, + { + "size": 1, + "treatment": "V87" + }, + { + "size": 1, + "treatment": "V88" + }, + { + "size": 1, + "treatment": "V89" + }, + { + "size": 1, + "treatment": "V90" + }, + { + "size": 1, + "treatment": "V91" + }, + { + "size": 1, + "treatment": "V92" + }, + { + "size": 1, + "treatment": "V93" + }, + { + "size": 1, + "treatment": "V94" + }, + { + "size": 1, + "treatment": "V95" + }, + { + "size": 1, + "treatment": "V96" + }, + { + "size": 1, + "treatment": "V97" + }, + { + "size": 1, + "treatment": "V98" + }, + { + "size": 1, + "treatment": "V99" + }, + { + "size": 1, + "treatment": "V100" } - ], - "combiner": "AND" - }, - "partitions": [ - { - "size": 1, - "treatment": "V1" - }, - { - "size": 1, - "treatment": "V2" - }, - { - "size": 1, - "treatment": "V3" - }, - { - "size": 1, - "treatment": "V4" - }, - { - "size": 1, - "treatment": "V5" - }, - { - "size": 1, - "treatment": "V6" - }, - { - "size": 1, - "treatment": "V7" - }, - { - "size": 1, - "treatment": "V8" - }, - { - "size": 1, - "treatment": "V9" - }, - { - "size": 1, - "treatment": "V10" - }, - { - "size": 1, - "treatment": "V11" - }, - { - "size": 1, - "treatment": "V12" - }, - { - "size": 1, - "treatment": "V13" - }, - { - "size": 1, - "treatment": "V14" - }, - { - "size": 1, - "treatment": "V15" - }, - { - "size": 1, - "treatment": "V16" - }, - { - "size": 1, - "treatment": "V17" - }, - { - "size": 1, - "treatment": "V18" - }, - { - "size": 1, - "treatment": "V19" - }, - { - "size": 1, - "treatment": "V20" - }, - { - "size": 1, - "treatment": "V21" - }, - { - "size": 1, - "treatment": "V22" - }, - { - "size": 1, - "treatment": "V23" - }, - { - "size": 1, - "treatment": "V24" - }, - { - "size": 1, - "treatment": "V25" - }, - { - "size": 1, - "treatment": "V26" - }, - { - "size": 1, - "treatment": "V27" - }, - { - "size": 1, - "treatment": "V28" - }, - { - "size": 1, - "treatment": "V29" - }, - { - "size": 1, - "treatment": "V30" - }, - { - "size": 1, - "treatment": "V31" - }, - { - "size": 1, - "treatment": "V32" - }, - { - "size": 1, - "treatment": "V33" - }, - { - "size": 1, - "treatment": "V34" - }, - { - "size": 1, - "treatment": "V35" - }, - { - "size": 1, - "treatment": "V36" - }, - { - "size": 1, - "treatment": "V37" - }, - { - "size": 1, - "treatment": "V38" - }, - { - "size": 1, - "treatment": "V39" - }, - { - "size": 1, - "treatment": "V40" - }, - { - "size": 1, - "treatment": "V41" - }, - { - "size": 1, - "treatment": "V42" - }, - { - "size": 1, - "treatment": "V43" - }, - { - "size": 1, - "treatment": "V44" - }, - { - "size": 1, - "treatment": "V45" - }, - { - "size": 1, - "treatment": "V46" - }, - { - "size": 1, - "treatment": "V47" - }, - { - "size": 1, - "treatment": "V48" - }, - { - "size": 1, - "treatment": "V49" - }, - { - "size": 1, - "treatment": "V50" - }, - { - "size": 1, - "treatment": "V51" - }, - { - "size": 1, - "treatment": "V52" - }, - { - "size": 1, - "treatment": "V53" - }, - { - "size": 1, - "treatment": "V54" - }, - { - "size": 1, - "treatment": "V55" - }, - { - "size": 1, - "treatment": "V56" - }, - { - "size": 1, - "treatment": "V57" - }, - { - "size": 1, - "treatment": "V58" - }, - { - "size": 1, - "treatment": "V59" - }, - { - "size": 1, - "treatment": "V60" - }, - { - "size": 1, - "treatment": "V61" - }, - { - "size": 1, - "treatment": "V62" - }, - { - "size": 1, - "treatment": "V63" - }, - { - "size": 1, - "treatment": "V64" - }, - { - "size": 1, - "treatment": "V65" - }, - { - "size": 1, - "treatment": "V66" - }, - { - "size": 1, - "treatment": "V67" - }, - { - "size": 1, - "treatment": "V68" - }, - { - "size": 1, - "treatment": "V69" - }, - { - "size": 1, - "treatment": "V70" - }, - { - "size": 1, - "treatment": "V71" - }, - { - "size": 1, - "treatment": "V72" - }, - { - "size": 1, - "treatment": "V73" - }, - { - "size": 1, - "treatment": "V74" - }, - { - "size": 1, - "treatment": "V75" - }, - { - "size": 1, - "treatment": "V76" - }, - { - "size": 1, - "treatment": "V77" - }, - { - "size": 1, - "treatment": "V78" - }, - { - "size": 1, - "treatment": "V79" - }, - { - "size": 1, - "treatment": "V80" - }, - { - "size": 1, - "treatment": "V81" - }, - { - "size": 1, - "treatment": "V82" - }, - { - "size": 1, - "treatment": "V83" - }, - { - "size": 1, - "treatment": "V84" - }, - { - "size": 1, - "treatment": "V85" - }, - { - "size": 1, - "treatment": "V86" - }, - { - "size": 1, - "treatment": "V87" - }, - { - "size": 1, - "treatment": "V88" - }, - { - "size": 1, - "treatment": "V89" - }, - { - "size": 1, - "treatment": "V90" - }, - { - "size": 1, - "treatment": "V91" - }, - { - "size": 1, - "treatment": "V92" - }, - { - "size": 1, - "treatment": "V93" - }, - { - "size": 1, - "treatment": "V94" - }, - { - "size": 1, - "treatment": "V95" - }, - { - "size": 1, - "treatment": "V96" - }, - { - "size": 1, - "treatment": "V97" - }, - { - "size": 1, - "treatment": "V98" - }, - { - "size": 1, - "treatment": "V99" - }, - { - "size": 1, - "treatment": "V100" - } - ] - } - ], - "defaultTreatment": "V1", - "trafficAllocation": 100, - "algo": 2, - "trafficTypeName": "account", - "status": "ACTIVE", - "killed": false - } - ], - "since":1506703262916, - "till":1506703262916 + ] + } + ], + "defaultTreatment": "V1", + "trafficAllocation": 100, + "algo": 2, + "trafficTypeName": "account", + "status": "ACTIVE", + "killed": false + } + ], + "since": 1506703262916, + "till": 1506703262916 + }, + "rbs": { + "d": [], + "s": 1506703262911, + "t": 1506703262911 + } } diff --git a/src/androidTest/assets/simple_split.json b/src/androidTest/assets/simple_split.json index c3e17e20b..009545ab8 100644 --- a/src/androidTest/assets/simple_split.json +++ b/src/androidTest/assets/simple_split.json @@ -1,107 +1,114 @@ { - "splits": [ - { - "trafficTypeName": "client", - "name": "workm", - "trafficAllocation": 100, - "trafficAllocationSeed": 147392224, - "seed": 524417105, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1602796638344, - "algo": 2, - "configurations": {}, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "splits": [ + { + "trafficTypeName": "client", + "name": "workm", + "trafficAllocation": 100, + "trafficAllocationSeed": 147392224, + "seed": 524417105, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1602796638344, + "algo": 2, + "configurations": {}, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "free", + "size": 100 + }, { - "keySelector": { - "trafficType": "client", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "new_segment" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "conta", + "size": 0 } - ] + ], + "label": "in segment new_segment" }, - "partitions": [ - { - "treatment": "on", - "size": 0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment": "off", - "size": 0 - }, - { - "treatment": "free", - "size": 100 - }, - { - "treatment": "conta", - "size": 0 - } - ], - "label": "in segment new_segment" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ + { + "treatment": "on", + "size": 100 + }, { - "keySelector": { - "trafficType": "client", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 0 + }, + { + "treatment": "free", + "size": 0 + }, + { + "treatment": "conta", + "size": 0 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - }, - { - "treatment": "off", - "size": 0 - }, - { - "treatment": "free", - "size": 0 - }, - { - "treatment": "conta", - "size": 0 - } - ], - "label": "default rule" - } - ] - } - ], - "since": 1602796638344, - "till": 1602796638344 + ], + "label": "default rule" + } + ] + } + ], + "since": 1602796638344, + "till": 1602796638344 + }, + "rbs": { + "d": [], + "s": 1602796638341, + "t": 1602796638341 + } } diff --git a/src/androidTest/assets/split_changes_1.json b/src/androidTest/assets/split_changes_1.json index 7eee38b79..abdd14bdd 100644 --- a/src/androidTest/assets/split_changes_1.json +++ b/src/androidTest/assets/split_changes_1.json @@ -1,2533 +1,2540 @@ { - "splits":[ - { - "trafficTypeName":"account", - "name":"FACUNDO_TEST", - "trafficAllocation":59, - "trafficAllocationSeed":-2108186082, - "seed":-1947050785, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1506703262916, - "algo":2, - "conditions":[ - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "nico_test", - "othertest" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null - } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "bla" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "ff": { + "splits": [ + { + "trafficTypeName": "account", + "name": "FACUNDO_TEST", + "trafficAllocation": 59, + "trafficAllocationSeed": -2108186082, + "seed": -1947050785, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1506703262916, + "algo": 2, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "nico_test", + "othertest" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"off", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"ALL_KEYS", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "bla" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "off", + "size": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"on", - "size":0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - }, - { - "treatment":"visa", - "size":0 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"account", - "name":"testing", - "trafficAllocation":100, - "trafficAllocationSeed":527505678, - "seed":-1716462249, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1506440189077, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"IN_SPLIT_TREATMENT", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":{ - "split":"test_definition_as_of", - "treatments":[ - "on" - ] - }, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 0 }, { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"IN_SPLIT_TREATMENT", - "negate":true, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":{ - "split":"Identify_UI", - "treatments":[ - "on" - ] - }, - "stringMatcherData":null + "treatment": "off", + "size": 100 + }, + { + "treatment": "visa", + "size": 0 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "account", + "name": "testing", + "trafficAllocation": 100, + "trafficAllocationSeed": 527505678, + "seed": -1716462249, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1506440189077, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "IN_SPLIT_TREATMENT", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": { + "split": "test_definition_as_of", + "treatments": [ + "on" + ] + }, + "stringMatcherData": null + }, + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "IN_SPLIT_TREATMENT", + "negate": true, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": { + "split": "Identify_UI", + "treatments": [ + "on" + ] + }, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":0 - } - ], - "label":"in split test_definition_as_of treatment [on] and not in split Identify_UI treatment [on]" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"IN_SPLIT_TREATMENT", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":{ - "split":"test_definition_as_of", - "treatments":[ - "off" - ] - }, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 } - ] + ], + "label": "in split test_definition_as_of treatment [on] and not in split Identify_UI treatment [on]" }, - "partitions":[ - { - "treatment":"on", - "size":0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "IN_SPLIT_TREATMENT", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": { + "split": "test_definition_as_of", + "treatments": [ + "off" + ] + }, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in split test_definition_as_of treatment [off]" - } - ] - }, - { - "trafficTypeName":"account", - "name":"testing222", - "trafficAllocation":100, - "trafficAllocationSeed":-397360967, - "seed":1058132210, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1505162627437, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "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 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 - }, - { - "treatment":"off", - "size":100 + ], + "label": "in split test_definition_as_of treatment [off]" + } + ] + }, + { + "trafficTypeName": "account", + "name": "testing222", + "trafficAllocation": 100, + "trafficAllocationSeed": -397360967, + "seed": 1058132210, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1505162627437, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"test222", - "size":0 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"account", - "name":"a_new_split_2", - "trafficAllocation":99, - "trafficAllocationSeed":-1349440646, - "seed":-1536389703, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1505161671620, - "algo":2, - "conditions":[ - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "adil", - "bb", - "bbb", - "dd3c0800-30f1-11e7-ba78-12395d4a9634", - "pato", - "tito" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null - } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"segment2" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null - } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted segment" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"test_copy" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null - } - ] - }, - "partitions":[ - { - "treatment":"off", - "size":100 - } - ], - "label":"whitelisted segment" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "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 + }, + { + "treatment": "test222", + "size": 0 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":52 - }, - { - "treatment":"off", - "size":48 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "account", + "name": "a_new_split_2", + "trafficAllocation": 99, + "trafficAllocationSeed": -1349440646, + "seed": -1536389703, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1505161671620, + "algo": 2, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "adil", + "bb", + "bbb", + "dd3c0800-30f1-11e7-ba78-12395d4a9634", + "pato", + "tito" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"testo", - "size":0 - } - ], - "label":"in segment all" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":"asda" - }, - "matcherType":"STARTS_WITH", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "ee", - "aa" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null - }, + "partitions": [ { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"IN_SEGMENT", - "negate":true, - "userDefinedSegmentMatcherData":{ - "segmentName":"segment2" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "treatment": "on", + "size": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"on", - "size":0 - }, - { - "treatment":"off", - "size":100 + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "segment2" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"testo", - "size":0 - } - ], - "label":"asda starts with [ee, aa] and not in segment segment2" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":"pp" - }, - "matcherType":"PART_OF_SET", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "pato", - "adil" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 100 } - ] + ], + "label": "whitelisted segment" }, - "partitions":[ - { - "treatment":"on", - "size":100 - }, - { - "treatment":"off", - "size":0 + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "test_copy" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"testo", - "size":0 - } - ], - "label":"pp part of [pato, adil]" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":"eee" - }, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "1", - "2", - "trevorrr" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "off", + "size": 100 } - ] + ], + "label": "whitelisted segment" }, - "partitions":[ - { - "treatment":"on", - "size":20 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":80 - }, - { - "treatment":"testo", - "size":0 - } - ], - "label":"eee in list [1, 2, ...]" - } - ] - }, - { - "trafficTypeName":"account", - "name":"test_string_without_attr", - "trafficAllocation":100, - "trafficAllocationSeed":-782597068, - "seed":-1682478887, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1504805281437, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"Segment3" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 52 }, { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "something" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "treatment": "off", + "size": 48 + }, + { + "treatment": "testo", + "size": 0 } - ] + ], + "label": "in segment all" }, - "partitions":[ - { - "treatment":"on", - "size":0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": "asda" + }, + "matcherType": "STARTS_WITH", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "ee", + "aa" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }, + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": true, + "userDefinedSegmentMatcherData": { + "segmentName": "segment2" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment Segment3 and in list [something]" - } - ] - }, - { - "trafficTypeName":"user", - "name":"Test", - "trafficAllocation":100, - "trafficAllocationSeed":217539832, - "seed":52164426, - "status":"ACTIVE", - "killed":true, - "defaultTreatment":"off", - "changeNumber":1504206031141, - "algo":2, - "configurations": { - "off": "{\"f1\":\"v1\"}" - }, - "conditions":[ - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"sample-segment" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null - } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted segment" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"demo" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + }, + { + "treatment": "testo", + "size": 0 } - ] + ], + "label": "asda starts with [ee, aa] and not in segment segment2" }, - "partitions":[ - { - "treatment":"off", - "size":100 - } - ], - "label":"whitelisted segment" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"employees" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": "pp" + }, + "matcherType": "PART_OF_SET", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "pato", + "adil" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "testo", + "size": 0 } - ] + ], + "label": "pp part of [pato, adil]" }, - "partitions":[ - { - "treatment":"off", - "size":100 - } - ], - "label":"whitelisted segment" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"user", - "attribute":null - }, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"sample-segment" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": "eee" + }, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "1", + "2", + "trevorrr" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 20 }, { - "keySelector":{ - "trafficType":"user", - "attribute":"fsdfsd" - }, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "a", - "b", - "c", - "d" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "treatment": "off", + "size": 80 }, { - "keySelector":{ - "trafficType":"user", - "attribute":"asdasdasd" - }, - "matcherType":"STARTS_WITH", - "negate":true, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "asdad", - "sa", - "das" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "treatment": "testo", + "size": 0 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":10 + ], + "label": "eee in list [1, 2, ...]" + } + ] + }, + { + "trafficTypeName": "account", + "name": "test_string_without_attr", + "trafficAllocation": 100, + "trafficAllocationSeed": -782597068, + "seed": -1682478887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1504805281437, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "Segment3" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }, + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "something" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":90 - } - ], - "label":"in segment sample-segment and fsdfsd in list [a, b, ...] and asdasdasd does not start with [asdad, sa, ...]" - } - ] - }, - { - "trafficTypeName":"account", - "name":"Test_Save_1", - "trafficAllocation":100, - "trafficAllocationSeed":-257595325, - "seed":-665945237, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1503956389520, - "algo":2, - "conditions":[ - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "1", - "12", - "123", - "23" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted" + ], + "label": "in segment Segment3 and in list [something]" + } + ] + }, + { + "trafficTypeName": "user", + "name": "Test", + "trafficAllocation": 100, + "trafficAllocationSeed": 217539832, + "seed": 52164426, + "status": "ACTIVE", + "killed": true, + "defaultTreatment": "off", + "changeNumber": 1504206031141, + "algo": 2, + "configurations": { + "off": "{\"f1\":\"v1\"}" }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "asd" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "sample-segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 } - ] + ], + "label": "whitelisted segment" }, - "partitions":[ - { - "treatment":"off", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"Segment3" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "demo" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "off", + "size": 100 } - ] + ], + "label": "whitelisted segment" }, - "partitions":[ - { - "treatment":"off", - "size":100 - } - ], - "label":"whitelisted segment" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"ALL_KEYS", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "employees" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "off", + "size": 100 } - ] + ], + "label": "whitelisted segment" }, - "partitions":[ - { - "treatment":"on", - "size":0 - }, - { - "treatment":"off", - "size":100 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "sample-segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }, + { + "keySelector": { + "trafficType": "user", + "attribute": "fsdfsd" + }, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "a", + "b", + "c", + "d" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }, + { + "keySelector": { + "trafficType": "user", + "attribute": "asdasdasd" + }, + "matcherType": "STARTS_WITH", + "negate": true, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "asdad", + "sa", + "das" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"v1", - "size":0 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"account", - "name":"TEST", - "trafficAllocation":100, - "trafficAllocationSeed":-673356676, - "seed":-511119211, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1503942404754, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "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": 10 + }, + { + "treatment": "off", + "size": 90 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 + ], + "label": "in segment sample-segment and fsdfsd in list [a, b, ...] and asdasdasd does not start with [asdad, sa, ...]" + } + ] + }, + { + "trafficTypeName": "account", + "name": "Test_Save_1", + "trafficAllocation": 100, + "trafficAllocationSeed": -257595325, + "seed": -665945237, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1503956389520, + "algo": 2, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "1", + "12", + "123", + "23" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":0 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"benchmark_jw_1", - "trafficAllocation":100, - "trafficAllocationSeed":987354894, - "seed":1292874260, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1503356075822, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"user", - "attribute":"atrib" - }, - "matcherType":"BETWEEN", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":{ - "dataType":"NUMBER", - "start":1474990940, - "end":1474990949 - }, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"on", - "size":95 + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "asd" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":5 - } - ], - "label":"atrib between 1474990940 and 1474990949" - }, - { - "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": "off", + "size": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"on", - "size":90 + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "Segment3" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":10 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"nico_tests", - "trafficAllocation":100, - "trafficAllocationSeed":1409699192, - "seed":-1997241870, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1501791316810, - "algo":2, - "conditions":[ - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "nico_test_browser__key" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "off", + "size": 100 } - ] + ], + "label": "whitelisted segment" }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"user", - "attribute":null - }, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"employees" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "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 + }, + { + "treatment": "v1", + "size": 0 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "account", + "name": "TEST", + "trafficAllocation": 100, + "trafficAllocationSeed": -673356676, + "seed": -511119211, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1503942404754, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":0 - } - ], - "label":"in segment employees" - } - ] - }, - { - "trafficTypeName":"account", - "name":"testo2222", - "trafficAllocation":100, - "trafficAllocationSeed":1164474086, - "seed":1270508512, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1501012403336, - "algo":2, - "conditions":[ - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "aasd" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 } - ] - }, - "partitions":[ - { - "treatment":"off", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"segment2" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "benchmark_jw_1", + "trafficAllocation": 100, + "trafficAllocationSeed": 987354894, + "seed": 1292874260, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1503356075822, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "atrib" + }, + "matcherType": "BETWEEN", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": { + "dataType": "NUMBER", + "start": 1474990940, + "end": 1474990949 + }, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 95 + }, + { + "treatment": "off", + "size": 5 } - ] + ], + "label": "atrib between 1474990940 and 1474990949" }, - "partitions":[ - { - "treatment":"off", - "size":100 - } - ], - "label":"whitelisted segment" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "ddddd" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + { + "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": 90 + }, + { + "treatment": "off", + "size": 10 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"Segment3" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "nico_tests", + "trafficAllocation": 100, + "trafficAllocationSeed": 1409699192, + "seed": -1997241870, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1501791316810, + "algo": 2, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "nico_test_browser__key" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted segment" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "ppp", - "ppppp" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "employees" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 } - ] - }, - "partitions":[ - { - "treatment":"pesto", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"IN_SEGMENT", - "negate":false, - "userDefinedSegmentMatcherData":{ - "segmentName":"test_copy" - }, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + ], + "label": "in segment employees" + } + ] + }, + { + "trafficTypeName": "account", + "name": "testo2222", + "trafficAllocation": 100, + "trafficAllocationSeed": 1164474086, + "seed": 1270508512, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1501012403336, + "algo": 2, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "aasd" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "off", + "size": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"pesto", - "size":100 - } - ], - "label":"whitelisted segment" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"ALL_KEYS", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "segment2" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "off", + "size": 100 } - ] + ], + "label": "whitelisted segment" }, - "partitions":[ - { - "treatment":"off", - "size":0 - }, - { - "treatment":"on", - "size":100 - }, - { - "treatment":"pesto", - "size":0 + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "ddddd" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"arse", - "size":0 - }, - { - "treatment":"zzzzick", - "size":0 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"Tagging", - "trafficAllocation":100, - "trafficAllocationSeed":1910132597, - "seed":-311493896, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1500590774768, - "algo":2, - "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": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"on", - "size":50 + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "Segment3" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":50 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"account", - "name":"Welcome_Page_UI", - "trafficAllocation":100, - "trafficAllocationSeed":1848523960, - "seed":1608586361, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1500577256901, - "algo":2, - "configurations": { - "off": "{\"the_emojis\":\"\uD83D\uDE01 -- áéíóúöÖüÜÏëç\"}" - }, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "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": 100 } - ] + ], + "label": "whitelisted segment" }, - "partitions":[ - { - "treatment":"on", - "size":0 + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "ppp", + "ppppp" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"test", - "name":"pato_test_3", - "trafficAllocation":100, - "trafficAllocationSeed":458647735, - "seed":95677506, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1500510847849, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"test", - "attribute":null - }, - "matcherType":"ALL_KEYS", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "pesto", + "size": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"on", - "size":0 + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "test_copy" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"account", - "name":"testo23", - "trafficAllocation":100, - "trafficAllocationSeed":-689658216, - "seed":1711156051, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1500064145947, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":"sdsd" - }, - "matcherType":"EQUAL_TO_BOOLEAN", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":true, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "pesto", + "size": 100 } - ] + ], + "label": "whitelisted segment" }, - "partitions":[ - { - "treatment":"on", - "size":0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"sdsd is true" - } - ] - }, - { - "trafficTypeName":"account", - "name":"testo909090", - "trafficAllocation":100, - "trafficAllocationSeed":-1196467266, - "seed":-1998101827, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1500039488369, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":"a" - }, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "a" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "off", + "size": 0 }, { - "keySelector":{ - "trafficType":"account", - "attribute":"v" - }, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "a" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "treatment": "on", + "size": 100 }, { - "keySelector":{ - "trafficType":"account", - "attribute":"asdadas" - }, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "a", - "b", - "c", - "d" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "treatment": "pesto", + "size": 0 }, { - "keySelector":{ - "trafficType":"account", - "attribute":"sds" - }, - "matcherType":"CONTAINS_STRING", - "negate":true, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "a", - "c", - "d" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "treatment": "arse", + "size": 0 }, { - "keySelector":{ - "trafficType":"account", - "attribute":"xcvxv" - }, - "matcherType":"EQUAL_TO", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":{ - "dataType":"NUMBER", - "value":122 - }, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "treatment": "zzzzick", + "size": 0 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "Tagging", + "trafficAllocation": 100, + "trafficAllocationSeed": 1910132597, + "seed": -311493896, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1500590774768, + "algo": 2, + "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 + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"a in list [a] and v in list [a] and asdadas in list [a, b, ...] and sds does not contain [a, c, ...] and xcvxv = 122" + "partitions": [ + { + "treatment": "on", + "size": 50 + }, + { + "treatment": "off", + "size": 50 + } + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "account", + "name": "Welcome_Page_UI", + "trafficAllocation": 100, + "trafficAllocationSeed": 1848523960, + "seed": 1608586361, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1500577256901, + "algo": 2, + "configurations": { + "off": "{\"the_emojis\":\"\uD83D\uDE01 -- áéíóúöÖüÜÏëç\"}" }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"ALL_KEYS", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "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 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "test", + "name": "pato_test_3", + "trafficAllocation": 100, + "trafficAllocationSeed": 458647735, + "seed": 95677506, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1500510847849, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "test", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":0 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"account", - "name":"testo22", - "trafficAllocation":100, - "trafficAllocationSeed":1223277820, - "seed":-1152948537, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1499721434259, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "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 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "account", + "name": "testo23", + "trafficAllocation": 100, + "trafficAllocationSeed": -689658216, + "seed": 1711156051, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1500064145947, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": "sdsd" + }, + "matcherType": "EQUAL_TO_BOOLEAN", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": true, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"test-net", - "trafficAllocation":100, - "trafficAllocationSeed":-2038196969, - "seed":-862203077, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1499718635999, - "algo":2, - "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 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "sdsd is true" + } + ] + }, + { + "trafficTypeName": "account", + "name": "testo909090", + "trafficAllocation": 100, + "trafficAllocationSeed": -1196467266, + "seed": -1998101827, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1500039488369, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": "a" + }, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "a" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }, + { + "keySelector": { + "trafficType": "account", + "attribute": "v" + }, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "a" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }, + { + "keySelector": { + "trafficType": "account", + "attribute": "asdadas" + }, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "a", + "b", + "c", + "d" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }, + { + "keySelector": { + "trafficType": "account", + "attribute": "sds" + }, + "matcherType": "CONTAINS_STRING", + "negate": true, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "a", + "c", + "d" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }, + { + "keySelector": { + "trafficType": "account", + "attribute": "xcvxv" + }, + "matcherType": "EQUAL_TO", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": { + "dataType": "NUMBER", + "value": 122 + }, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"account", - "name":"test_dep_2", - "trafficAllocation":100, - "trafficAllocationSeed":-806171485, - "seed":922684950, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1499707910800, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"IN_SPLIT_TREATMENT", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":{ - "split":"Identify_UI", - "treatments":[ - "on" - ] - }, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 } - ] + ], + "label": "a in list [a] and v in list [a] and asdadas in list [a, b, ...] and sds does not contain [a, c, ...] and xcvxv = 122" }, - "partitions":[ - { - "treatment":"on", - "size":0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in split Identify_UI treatment [on]" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"IN_SPLIT_TREATMENT", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":{ - "split":"Definition_As_Of_Clickable_UI", - "treatments":[ - "off" - ] - }, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":50 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "account", + "name": "testo22", + "trafficAllocation": 100, + "trafficAllocationSeed": 1223277820, + "seed": -1152948537, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1499721434259, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":50 - } - ], - "label":"in split Definition_As_Of_Clickable_UI treatment [off]" - } - ] - }, - { - "trafficTypeName":"account", - "name":"Definition_As_Of_Clickable_UI", - "trafficAllocation":100, - "trafficAllocationSeed":-198035199, - "seed":-151947071, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1498168847351, - "algo":2, - "conditions":[ - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "tito", - "trevor" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"ALL_KEYS", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "test-net", + "trafficAllocation": 100, + "trafficAllocationSeed": -2038196969, + "seed": -862203077, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1499718635999, + "algo": 2, + "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 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "account", + "name": "test_dep_2", + "trafficAllocation": 100, + "trafficAllocationSeed": -806171485, + "seed": 922684950, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1499707910800, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "IN_SPLIT_TREATMENT", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": { + "split": "Identify_UI", + "treatments": [ + "on" + ] + }, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"account", - "name":"Identify_UI", - "trafficAllocation":100, - "trafficAllocationSeed":-139516103, - "seed":1543172523, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1498078888450, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "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": "in split Identify_UI treatment [on]" }, - "partitions":[ - { - "treatment":"on", - "size":100 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "IN_SPLIT_TREATMENT", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": { + "split": "Definition_As_Of_Clickable_UI", + "treatments": [ + "off" + ] + }, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":0 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"account", - "name":"test_definition_as_of", - "trafficAllocation":100, - "trafficAllocationSeed":1025823325, - "seed":-554248124, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1497289730024, - "algo":2, - "conditions":[ - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"ALL_KEYS", - "negatee":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 50 + }, + { + "treatment": "off", + "size": 50 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "in split Definition_As_Of_Clickable_UI treatment [off]" + } + ] + }, + { + "trafficTypeName": "account", + "name": "Definition_As_Of_Clickable_UI", + "trafficAllocation": 100, + "trafficAllocationSeed": -198035199, + "seed": -151947071, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1498168847351, + "algo": 2, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "tito", + "trevor" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"Test-jw-go", - "trafficAllocation":100, - "trafficAllocationSeed":768122971, - "seed":1539205707, - "status":"ACTIVE", - "defaultTreatment":"off", - "changeNumber":1496339112852, - "algo":2, - "conditions":[ - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "test1" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null + "partitions": [ + { + "treatment": "on", + "size": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "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 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "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 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "account", + "name": "Identify_UI", + "trafficAllocation": 100, + "trafficAllocationSeed": -139516103, + "seed": 1543172523, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1498078888450, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"benchmark_jw_7", - "trafficAllocation":100, - "trafficAllocationSeed":-1340337178, - "seed":-1091938685, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1494593464885, - "algo":2, - "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": 100 + }, + { + "treatment": "off", + "size": 0 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "account", + "name": "test_definition_as_of", + "trafficAllocation": 100, + "trafficAllocationSeed": 1025823325, + "seed": -554248124, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1497289730024, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negatee": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"benchmark_jw_6", - "trafficAllocation":100, - "trafficAllocationSeed":-1202331834, - "seed":-48445256, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1494593448028, - "algo":2, - "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 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "Test-jw-go", + "trafficAllocation": 100, + "trafficAllocationSeed": 768122971, + "seed": 1539205707, + "status": "ACTIVE", + "defaultTreatment": "off", + "changeNumber": 1496339112852, + "algo": 2, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "test1" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"benchmark_jw_5", - "trafficAllocation":100, - "trafficAllocationSeed":2119994290, - "seed":-227092192, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1494593428034, - "algo":2, - "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": 100 } - ] + ], + "label": "whitelisted" }, - "partitions":[ - { - "treatment":"on", - "size":0 + { + "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 + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"benchmark_jw_4", - "trafficAllocation":100, - "trafficAllocationSeed":1066635158, - "seed":-850704283, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1494593412226, - "algo":2, - "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 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "benchmark_jw_7", + "trafficAllocation": 100, + "trafficAllocationSeed": -1340337178, + "seed": -1091938685, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1494593464885, + "algo": 2, + "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 + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"benchmark_jw_3", - "trafficAllocation":100, - "trafficAllocationSeed":1252392550, - "seed":971538037, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1494593352077, - "algo":2, - "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 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "benchmark_jw_6", + "trafficAllocation": 100, + "trafficAllocationSeed": -1202331834, + "seed": -48445256, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1494593448028, + "algo": 2, + "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 + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"benchmark_jw_2", - "trafficAllocation":100, - "trafficAllocationSeed":-285565213, - "seed":-1992295819, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1494593336752, - "algo":2, - "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 } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "benchmark_jw_5", + "trafficAllocation": 100, + "trafficAllocationSeed": 2119994290, + "seed": -227092192, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1494593428034, + "algo": 2, + "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": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "benchmark_jw_4", + "trafficAllocation": 100, + "trafficAllocationSeed": 1066635158, + "seed": -850704283, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1494593412226, + "algo": 2, + "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 + } + ] }, - { - "treatment":"off", - "size":100 - } - ], - "label":"in segment all" - } - ] - }, - { - "trafficTypeName":"user", - "name":"broken_split", - "trafficAllocation":100, - "trafficAllocationSeed":-285565213, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1494593336752 - } - ], - "since":1506703262916, - "till":1506703262916 + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + } + ], + "label": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "benchmark_jw_3", + "trafficAllocation": 100, + "trafficAllocationSeed": 1252392550, + "seed": 971538037, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1494593352077, + "algo": 2, + "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": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "benchmark_jw_2", + "trafficAllocation": 100, + "trafficAllocationSeed": -285565213, + "seed": -1992295819, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1494593336752, + "algo": 2, + "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": "in segment all" + } + ] + }, + { + "trafficTypeName": "user", + "name": "broken_split", + "trafficAllocation": 100, + "trafficAllocationSeed": -285565213, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1494593336752 + } + ], + "since": 1506703262916, + "till": 1506703262916 + }, + "rbs": { + "d": [], + "s": 1506703262911, + "t": 1506703262911 + } } \ No newline at end of file diff --git a/src/androidTest/assets/split_changes_large_segments-0.json b/src/androidTest/assets/split_changes_large_segments-0.json index 4c3e613cb..8d78431e5 100644 --- a/src/androidTest/assets/split_changes_large_segments-0.json +++ b/src/androidTest/assets/split_changes_large_segments-0.json @@ -1,55 +1,56 @@ -{"ff":{ - "splits": [ - { - "trafficTypeName": "user", - "name": "ls_split", - "trafficAllocation": 100, - "trafficAllocationSeed": -285565213, - "seed": -1992295819, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1506703262916, - "algo": 2, - "conditions": [ - { - "conditionType": "WHITELIST", - "matcherGroup": { - "combiner": "AND", - "matchers": [ +{ + "ff": { + "splits": [ + { + "trafficTypeName": "user", + "name": "ls_split", + "trafficAllocation": 100, + "trafficAllocationSeed": -285565213, + "seed": -1992295819, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1506703262916, + "algo": 2, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_LARGE_SEGMENT", + "negate": false, + "userDefinedLargeSegmentMatcherData": { + "largeSegmentName": "large-segment1" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": null, - "matcherType": "IN_LARGE_SEGMENT", - "negate": false, - "userDefinedLargeSegmentMatcherData": { - "largeSegmentName": "large-segment1" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "on", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ], - "label": "whitelisted segment" - } - ] - } - ], - "since": 1506703262916, - "till": 1506703262916 -}, -"rbs": { - "d": [], - "s": 1506703262911, - "t": 1506703262911 -} + ], + "label": "whitelisted segment" + } + ] + } + ], + "since": 1506703262916, + "till": 1506703262916 + }, + "rbs": { + "d": [], + "s": 1506703262911, + "t": 1506703262911 + } } diff --git a/src/androidTest/java/helper/IntegrationHelper.java b/src/androidTest/java/helper/IntegrationHelper.java index a8c0182c1..ba3b7f064 100644 --- a/src/androidTest/java/helper/IntegrationHelper.java +++ b/src/androidTest/java/helper/IntegrationHelper.java @@ -318,7 +318,7 @@ public static String splitKill(String changeNumber, String splitName) { public static String loadSplitChanges(Context context, String fileName) { FileHelper fileHelper = new FileHelper(); String change = fileHelper.loadFileContent(context, fileName); - SplitChange parsedChange = Json.fromJson(change, SplitChange.class); + SplitChange parsedChange = Json.fromJson(change, TargetingRulesChange.class).getFeatureFlagsChange(); parsedChange.since = parsedChange.till; return Json.toJson(TargetingRulesChange.create(parsedChange)); } @@ -424,6 +424,14 @@ static Map parse(String query) throws UnsupportedEncodingExcepti return queryPairs; } + public static SplitChange getChangeFromJsonString(String json) { + return Json.fromJson(json, TargetingRulesChange.class).getFeatureFlagsChange(); + } + + public static String splitChangesJsonToTargetingRulesChangeJson(String change) { + return Json.toJson(TargetingRulesChange.create(Json.fromJson(change, SplitChange.class))); + } + /** * A simple interface to allow us to define the response for a given path */ diff --git a/src/androidTest/java/tests/integration/FlagsSpecInRequestTest.java b/src/androidTest/java/tests/integration/FlagsSpecInRequestTest.java index 040d732d8..352c96069 100644 --- a/src/androidTest/java/tests/integration/FlagsSpecInRequestTest.java +++ b/src/androidTest/java/tests/integration/FlagsSpecInRequestTest.java @@ -194,7 +194,8 @@ private SplitFactory initSplitFactory(TestableSplitConfigBuilder builder, HttpCl } private String loadSplitChanges() { - return IntegrationHelper.loadSplitChanges(mContext, "split_changes_1.json"); + String changes = IntegrationHelper.loadSplitChanges(mContext, "split_changes_1.json"); + return changes; } private static SplitEntity newSplitEntity(String name, String trafficType, Set sets) { diff --git a/src/androidTest/java/tests/integration/IntegrationTest.java b/src/androidTest/java/tests/integration/IntegrationTest.java index 07f8d8c37..1aa8752de 100644 --- a/src/androidTest/java/tests/integration/IntegrationTest.java +++ b/src/androidTest/java/tests/integration/IntegrationTest.java @@ -303,7 +303,7 @@ public void testGetTreatmentFromCache() throws Exception { mRoomDb = DatabaseHelper.getTestDatabase(mContext); mRoomDb.generalInfoDao().update(new GeneralInfoEntity(GeneralInfoEntity.CHANGE_NUMBER_INFO, 10)); - SplitChange change = Json.fromJson(mJsonChanges.get(0), SplitChange.class); + SplitChange change = IntegrationHelper.getChangeFromJsonString(mJsonChanges.get(0)); List entities = new ArrayList<>(); for (Split split : change.splits) { String splitName = split.name; diff --git a/src/androidTest/java/tests/integration/MySegmentUpdatedTest.java b/src/androidTest/java/tests/integration/MySegmentUpdatedTest.java index 34b5dcc25..59bc105df 100644 --- a/src/androidTest/java/tests/integration/MySegmentUpdatedTest.java +++ b/src/androidTest/java/tests/integration/MySegmentUpdatedTest.java @@ -115,10 +115,10 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio isFirstChangesReq = false; String change = mJsonChanges.get(0); return new MockResponse().setResponseCode(200) - .setBody(change); + .setBody(IntegrationHelper.splitChangesJsonToTargetingRulesChangeJson(change)); } return new MockResponse().setResponseCode(200) - .setBody(emptyChanges()); + .setBody(IntegrationHelper.splitChangesJsonToTargetingRulesChangeJson(emptyChanges())); } else if (request.getPath().contains("/testImpressions/bulk")) { @@ -215,7 +215,7 @@ private void loadSplitChanges() { mJsonChanges = new ArrayList<>(); String jsonChange = fileHelper.loadFileContent(mContext, "splitchanges_int_test.json"); - SplitChange change = Json.fromJson(jsonChange, SplitChange.class); + SplitChange change = IntegrationHelper.getChangeFromJsonString(jsonChange); Split split = change.splits.get(0); split.changeNumber = change.since + 1; diff --git a/src/androidTest/java/tests/integration/MySegmentsServerErrorTest.java b/src/androidTest/java/tests/integration/MySegmentsServerErrorTest.java index ab1dc376b..a42fc79ec 100644 --- a/src/androidTest/java/tests/integration/MySegmentsServerErrorTest.java +++ b/src/androidTest/java/tests/integration/MySegmentsServerErrorTest.java @@ -34,6 +34,7 @@ import io.split.android.client.dtos.Partition; import io.split.android.client.dtos.Split; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.dtos.TestImpressions; import io.split.android.client.dtos.UserDefinedSegmentMatcherData; import io.split.android.client.events.SplitEvent; @@ -118,7 +119,7 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio .setBody(change); } return new MockResponse().setResponseCode(200) - .setBody(emptyChanges()); + .setBody(IntegrationHelper.emptySplitChanges(9567456937869L)); } else if (request.getPath().contains("/testImpressions/bulk")) { @@ -208,7 +209,7 @@ private void loadSplitChanges() { mJsonChanges = new ArrayList<>(); String jsonChange = fileHelper.loadFileContent(mContext, "splitchanges_int_test.json"); - SplitChange change = Json.fromJson(jsonChange, SplitChange.class); + SplitChange change = IntegrationHelper.getChangeFromJsonString(jsonChange); Split split = change.splits.get(0); split.changeNumber = change.since + 1; @@ -232,7 +233,7 @@ private void loadSplitChanges() { split.conditions.add(0, inSegmentOneCondition); split.conditions.add(0, inSegmentTwoCondition); - mJsonChanges.add(Json.toJson(change)); + mJsonChanges.add(Json.toJson(TargetingRulesChange.create(change))); } private Condition inSegmentCondition(String name) { diff --git a/src/androidTest/java/tests/integration/SplitChangesCdnBypassTest.java b/src/androidTest/java/tests/integration/SplitChangesCdnBypassTest.java index 2edadda5b..76d09accc 100644 --- a/src/androidTest/java/tests/integration/SplitChangesCdnBypassTest.java +++ b/src/androidTest/java/tests/integration/SplitChangesCdnBypassTest.java @@ -136,7 +136,7 @@ public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) { } else if (uri.getQuery().contains("since=3")) { return getSplitsMockResponse("3", "3"); } - return new HttpResponseMock(200, "{\"splits\":[], \"since\": 4, \"till\": 4 }"); + return new HttpResponseMock(200, IntegrationHelper.emptySplitChanges(4, 4)); } else if (uri.getPath().contains("/testImpressions/bulk")) { return new HttpResponseMock(200); } else if (uri.getPath().contains("/auth")) { @@ -151,7 +151,7 @@ public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) { @NonNull private HttpResponseMock getSplitsMockResponse(final String since, final String till) { - return new HttpResponseMock(200, "{\"splits\":[], \"since\": " + since + ", \"till\": " + till + " }"); + return new HttpResponseMock(200, IntegrationHelper.emptySplitChanges(Long.parseLong(since), Long.parseLong(till))); } private HttpStreamResponseMock createStreamResponse(int status, BlockingQueue streamingResponseData) throws IOException { diff --git a/src/androidTest/java/tests/integration/SplitChangesServerErrorTest.java b/src/androidTest/java/tests/integration/SplitChangesServerErrorTest.java index 562939151..28fe7285f 100644 --- a/src/androidTest/java/tests/integration/SplitChangesServerErrorTest.java +++ b/src/androidTest/java/tests/integration/SplitChangesServerErrorTest.java @@ -32,6 +32,7 @@ import io.split.android.client.dtos.Partition; import io.split.android.client.dtos.Split; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.events.SplitEvent; import io.split.android.client.network.HttpMethod; import io.split.android.client.storage.db.SplitRoomDatabase; @@ -175,7 +176,7 @@ private void loadSplitChanges() { String jsonChange = fileHelper.loadFileContent(mContext, "splitchanges_int_test.json"); long prevChangeNumber = 0; for (int i = 0; i < 3; i++) { - SplitChange change = Json.fromJson(jsonChange, SplitChange.class); + SplitChange change = Json.fromJson(jsonChange, TargetingRulesChange.class).getFeatureFlagsChange(); if (prevChangeNumber != 0) { change.since = prevChangeNumber + CHANGE_INTERVAL; change.till = prevChangeNumber + CHANGE_INTERVAL; diff --git a/src/androidTest/java/tests/integration/SplitChangesTest.java b/src/androidTest/java/tests/integration/SplitChangesTest.java index 635595158..f399aeb98 100644 --- a/src/androidTest/java/tests/integration/SplitChangesTest.java +++ b/src/androidTest/java/tests/integration/SplitChangesTest.java @@ -94,7 +94,7 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio mLatchs.get(currReq - 1).countDown(); } String changes = mJsonChanges.get(currReq); - return new MockResponse().setResponseCode(200).setBody(changes); + return new MockResponse().setResponseCode(200).setBody(IntegrationHelper.splitChangesJsonToTargetingRulesChangeJson(changes)); } else if (currReq == mLatchs.size()) { mLatchs.get(currReq - 1).countDown(); } @@ -208,7 +208,7 @@ private void loadSplitChanges() { String jsonChange = fileHelper.loadFileContent(mContext, "splitchanges_int_test.json"); long prevChangeNumber = 0; for (int i = 0; i < 4; i++) { - SplitChange change = Json.fromJson(jsonChange, SplitChange.class); + SplitChange change = IntegrationHelper.getChangeFromJsonString(jsonChange); if (prevChangeNumber != 0) { change.since = prevChangeNumber; change.till = prevChangeNumber + CHANGE_INTERVAL; diff --git a/src/androidTest/java/tests/integration/SplitsTwoDifferentApiKeyTest.java b/src/androidTest/java/tests/integration/SplitsTwoDifferentApiKeyTest.java index 548daad4d..2556d2f27 100644 --- a/src/androidTest/java/tests/integration/SplitsTwoDifferentApiKeyTest.java +++ b/src/androidTest/java/tests/integration/SplitsTwoDifferentApiKeyTest.java @@ -224,8 +224,8 @@ private String loadMockedData(String fileName) { } private void loadSplitChanges() { - mSplitChange = Json.fromJson( - loadMockedData("splitchanges_int_test.json"), SplitChange.class); + mSplitChange = IntegrationHelper.getChangeFromJsonString( + loadMockedData("splitchanges_int_test.json")); } private String getSplitChanges(int factoryNumber, int hitNumber) { diff --git a/src/androidTest/java/tests/integration/TrackTest.java b/src/androidTest/java/tests/integration/TrackTest.java index 550aac3dd..968ee3459 100644 --- a/src/androidTest/java/tests/integration/TrackTest.java +++ b/src/androidTest/java/tests/integration/TrackTest.java @@ -262,7 +262,7 @@ public void largeNumberInPropertiesTest() throws InterruptedException, SplitInst } private String emptyChanges() { - return "{\"splits\":[], \"since\": 9567456937869, \"till\": 9567456937869 }"; + return IntegrationHelper.emptySplitChanges(9567456937869L, 9567456937869L); } private Event findEvent(String type, Double value) { diff --git a/src/androidTest/java/tests/integration/attributes/AttributesIntegrationTest.java b/src/androidTest/java/tests/integration/attributes/AttributesIntegrationTest.java index 949d415aa..ebaeea4fe 100644 --- a/src/androidTest/java/tests/integration/attributes/AttributesIntegrationTest.java +++ b/src/androidTest/java/tests/integration/attributes/AttributesIntegrationTest.java @@ -279,7 +279,7 @@ private List getSplitListFromJson() { FileHelper fileHelper = new FileHelper(); String s = fileHelper.loadFileContent(mContext, "attributes_test_split_change.json"); - SplitChange changes = Json.fromJson(s, SplitChange.class); + SplitChange changes = IntegrationHelper.getChangeFromJsonString(s); return changes.splits; } diff --git a/src/androidTest/java/tests/integration/encryption/EncryptionTest.java b/src/androidTest/java/tests/integration/encryption/EncryptionTest.java index b0946aa05..8d9abd31b 100644 --- a/src/androidTest/java/tests/integration/encryption/EncryptionTest.java +++ b/src/androidTest/java/tests/integration/encryption/EncryptionTest.java @@ -360,7 +360,7 @@ private Map getResponses() { private String loadSplitChanges() { FileHelper fileHelper = new FileHelper(); String change = fileHelper.loadFileContent(mContext, "split_changes_1.json"); - SplitChange parsedChange = Json.fromJson(change, SplitChange.class); + SplitChange parsedChange = IntegrationHelper.getChangeFromJsonString(change); parsedChange.splits = parsedChange.splits.stream().filter(s -> s.name.equals("FACUNDO_TEST") || s.name.equals("testing")).collect(Collectors.toList()); parsedChange.since = parsedChange.till; diff --git a/src/androidTest/java/tests/integration/largesegments/LargeSegmentTestHelper.java b/src/androidTest/java/tests/integration/largesegments/LargeSegmentTestHelper.java index 95cbf86bf..3e30292f5 100644 --- a/src/androidTest/java/tests/integration/largesegments/LargeSegmentTestHelper.java +++ b/src/androidTest/java/tests/integration/largesegments/LargeSegmentTestHelper.java @@ -152,7 +152,7 @@ protected SplitClient getReadyClient(String matchingKey, SplitFactory factory) { private String splitChangesLargeSegments(long since, long till) { String change = mFileHelper.loadFileContent(mContext, "split_changes_large_segments-0.json"); - SplitChange parsedChange = Json.fromJson(change, SplitChange.class); + SplitChange parsedChange = IntegrationHelper.getChangeFromJsonString(change); parsedChange.since = since; parsedChange.till = till; diff --git a/src/androidTest/java/tests/integration/largesegments/LargeSegmentsStreamingTest.java b/src/androidTest/java/tests/integration/largesegments/LargeSegmentsStreamingTest.java index c5bc39732..30d67a2e3 100644 --- a/src/androidTest/java/tests/integration/largesegments/LargeSegmentsStreamingTest.java +++ b/src/androidTest/java/tests/integration/largesegments/LargeSegmentsStreamingTest.java @@ -186,7 +186,7 @@ private HttpResponseMockDispatcher buildDispatcher() { private String splitChangesLargeSegments(long since, long till) { String change = mFileHelper.loadFileContent(mContext, "split_changes_large_segments-0.json"); - SplitChange parsedChange = Json.fromJson(change, SplitChange.class); + SplitChange parsedChange = IntegrationHelper.getChangeFromJsonString(change); parsedChange.since = since; parsedChange.till = till; diff --git a/src/androidTest/java/tests/integration/rollout/RolloutCacheManagerIntegrationTest.java b/src/androidTest/java/tests/integration/rollout/RolloutCacheManagerIntegrationTest.java index be821fde0..2f7d763eb 100644 --- a/src/androidTest/java/tests/integration/rollout/RolloutCacheManagerIntegrationTest.java +++ b/src/androidTest/java/tests/integration/rollout/RolloutCacheManagerIntegrationTest.java @@ -298,7 +298,7 @@ private List getSplitListFromJson() { FileHelper fileHelper = new FileHelper(); String s = fileHelper.loadFileContent(mContext, "attributes_test_split_change.json"); - SplitChange changes = Json.fromJson(s, SplitChange.class); + SplitChange changes = IntegrationHelper.getChangeFromJsonString(s); return changes.splits; } diff --git a/src/androidTest/java/tests/integration/sets/FlagSetsEvaluationTest.java b/src/androidTest/java/tests/integration/sets/FlagSetsEvaluationTest.java index 0f77dbc92..387b7da80 100644 --- a/src/androidTest/java/tests/integration/sets/FlagSetsEvaluationTest.java +++ b/src/androidTest/java/tests/integration/sets/FlagSetsEvaluationTest.java @@ -164,7 +164,7 @@ private SplitClient getClient( private String loadSplitChangeWithSet(int setsCount) { String change = mFileHelper.loadFileContent(mContext, "split_changes_flag_set-" + setsCount + ".json"); - SplitChange parsedChange = Json.fromJson(change, SplitChange.class); + SplitChange parsedChange = IntegrationHelper.getChangeFromJsonString(change); parsedChange.since = parsedChange.till; return Json.toJson(TargetingRulesChange.create(parsedChange)); diff --git a/src/androidTest/java/tests/integration/sets/FlagSetsMultipleFactoryTest.java b/src/androidTest/java/tests/integration/sets/FlagSetsMultipleFactoryTest.java index c5e0609c2..49a1ffc1e 100644 --- a/src/androidTest/java/tests/integration/sets/FlagSetsMultipleFactoryTest.java +++ b/src/androidTest/java/tests/integration/sets/FlagSetsMultipleFactoryTest.java @@ -126,7 +126,7 @@ private List getNamesFromDb(String dbName) { private String loadSplitChangeWithSet(int setsCount) { String change = mFileHelper.loadFileContent(mContext, "split_changes_flag_set-" + setsCount + ".json"); - SplitChange parsedChange = Json.fromJson(change, SplitChange.class); + SplitChange parsedChange = IntegrationHelper.getChangeFromJsonString(change); parsedChange.since = parsedChange.till; return Json.toJson(TargetingRulesChange.create(parsedChange)); diff --git a/src/androidTest/java/tests/integration/sets/FlagSetsPollingTest.java b/src/androidTest/java/tests/integration/sets/FlagSetsPollingTest.java index b7ddb3169..ef8543406 100644 --- a/src/androidTest/java/tests/integration/sets/FlagSetsPollingTest.java +++ b/src/androidTest/java/tests/integration/sets/FlagSetsPollingTest.java @@ -227,7 +227,7 @@ private SplitFactory createFactory( private String loadSplitChangeWithSet(int setsCount) { String change = fileHelper.loadFileContent(mContext, "split_changes_flag_set-" + setsCount + ".json"); - SplitChange parsedChange = Json.fromJson(change, SplitChange.class); + SplitChange parsedChange = IntegrationHelper.getChangeFromJsonString(change); parsedChange.since = parsedChange.till; return Json.toJson(TargetingRulesChange.create(parsedChange)); diff --git a/src/androidTest/java/tests/integration/shared/SharedClientsIntegrationTest.java b/src/androidTest/java/tests/integration/shared/SharedClientsIntegrationTest.java index a12642bee..a374460b7 100644 --- a/src/androidTest/java/tests/integration/shared/SharedClientsIntegrationTest.java +++ b/src/androidTest/java/tests/integration/shared/SharedClientsIntegrationTest.java @@ -261,7 +261,7 @@ private void loadSplitChanges() { } private void insertSplitsIntoDb() { - SplitChange change = Json.fromJson(mJsonChanges.get(0), SplitChange.class); + SplitChange change = IntegrationHelper.getChangeFromJsonString(mJsonChanges.get(0)); List entities = new ArrayList<>(); for (Split split : change.splits) { String splitName = split.name; diff --git a/src/androidTest/java/tests/integration/streaming/ImpressionsCountTest.java b/src/androidTest/java/tests/integration/streaming/ImpressionsCountTest.java index d816ee747..d64bff001 100644 --- a/src/androidTest/java/tests/integration/streaming/ImpressionsCountTest.java +++ b/src/androidTest/java/tests/integration/streaming/ImpressionsCountTest.java @@ -228,14 +228,14 @@ public HttpStreamResponseMock getStreamResponse(URI uri) { } private void loadSplitChanges() { - mSplitChange = Json.fromJson( - loadMockedData("splitchanges_int_test.json"), SplitChange.class); + mSplitChange = IntegrationHelper.getChangeFromJsonString( + loadMockedData("splitchanges_int_test.json")); - SplitChange c1 = Json.fromJson( - loadMockedData("splitchanges_int_test.json"), SplitChange.class); + SplitChange c1 = IntegrationHelper.getChangeFromJsonString( + loadMockedData("splitchanges_int_test.json")); - SplitChange c2 = Json.fromJson( - loadMockedData("splitchanges_int_test.json"), SplitChange.class); + SplitChange c2 = IntegrationHelper.getChangeFromJsonString( + loadMockedData("splitchanges_int_test.json")); mSplitChange.splits.get(0).name = "SPLIT_1"; Split split = c1.splits.get(0); diff --git a/src/androidTest/java/tests/integration/streaming/SdkUpdateStreamingTest.java b/src/androidTest/java/tests/integration/streaming/SdkUpdateStreamingTest.java index 07ac91545..55ce985a8 100644 --- a/src/androidTest/java/tests/integration/streaming/SdkUpdateStreamingTest.java +++ b/src/androidTest/java/tests/integration/streaming/SdkUpdateStreamingTest.java @@ -407,8 +407,8 @@ private void pushInitialId() { } private String getChanges(String treatment, long since, long till) { - SplitChange change = Json.fromJson( - loadMockedData("splitchanges_int_test.json"), SplitChange.class); + SplitChange change = IntegrationHelper.getChangeFromJsonString( + loadMockedData("splitchanges_int_test.json")); change.since = since; change.till = till; Split split = change.splits.get(0); diff --git a/src/androidTest/java/tests/integration/streaming/SplitsKillProcessTest.java b/src/androidTest/java/tests/integration/streaming/SplitsKillProcessTest.java index 45b0369d5..d884324f8 100644 --- a/src/androidTest/java/tests/integration/streaming/SplitsKillProcessTest.java +++ b/src/androidTest/java/tests/integration/streaming/SplitsKillProcessTest.java @@ -221,13 +221,12 @@ private void pushMessage(String fileName) { } private void loadSplitChanges() { - SplitChange change = Json.fromJson( - loadMockedData("splitchanges_int_test.json"), SplitChange.class); + SplitChange change = IntegrationHelper.getChangeFromJsonString("splitchanges_int_test.json"); Split split = change.splits.get(0); split.name = "test_feature_1"; - mSplitChange = Json.fromJson( - loadMockedData("splitchanges_int_test.json"), SplitChange.class); + mSplitChange = IntegrationHelper.getChangeFromJsonString( + loadMockedData("splitchanges_int_test.json")); mSplitChange.splits.add(split); } diff --git a/src/androidTest/java/tests/integration/streaming/SplitsSyncProcessTest.java b/src/androidTest/java/tests/integration/streaming/SplitsSyncProcessTest.java index 3675b4612..f5eae5dc1 100644 --- a/src/androidTest/java/tests/integration/streaming/SplitsSyncProcessTest.java +++ b/src/androidTest/java/tests/integration/streaming/SplitsSyncProcessTest.java @@ -214,8 +214,8 @@ private void pushMessage(String fileName) { } private void loadSplitChanges() { - mSplitChange = Json.fromJson( - loadMockedData("splitchanges_int_test.json"), SplitChange.class); + mSplitChange = IntegrationHelper.getChangeFromJsonString( + loadMockedData("splitchanges_int_test.json")); } private String getSplitChanges(int hit) { diff --git a/src/androidTest/java/tests/integration/telemetry/TelemetryIntegrationTest.java b/src/androidTest/java/tests/integration/telemetry/TelemetryIntegrationTest.java index d843a6362..7aea63f99 100644 --- a/src/androidTest/java/tests/integration/telemetry/TelemetryIntegrationTest.java +++ b/src/androidTest/java/tests/integration/telemetry/TelemetryIntegrationTest.java @@ -375,7 +375,7 @@ private List getSplitListFromJson() { FileHelper fileHelper = new FileHelper(); String s = fileHelper.loadFileContent(mContext, "splitchanges_int_test.json"); - SplitChange changes = Json.fromJson(s, SplitChange.class); + SplitChange changes = IntegrationHelper.getChangeFromJsonString(s); return changes.splits; } diff --git a/src/androidTest/java/tests/service/SdkUpdatePollingTest.java b/src/androidTest/java/tests/service/SdkUpdatePollingTest.java index ee20e6bd6..e04afdbe4 100644 --- a/src/androidTest/java/tests/service/SdkUpdatePollingTest.java +++ b/src/androidTest/java/tests/service/SdkUpdatePollingTest.java @@ -36,6 +36,7 @@ import io.split.android.client.dtos.Partition; import io.split.android.client.dtos.Split; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.events.SplitEvent; import io.split.android.client.network.HttpMethod; import io.split.android.client.storage.db.GeneralInfoEntity; @@ -347,8 +348,8 @@ private void pushInitialId() { } private String getChanges(String treatment, long since, long till) { - SplitChange change = Json.fromJson( - loadMockedData("splitchanges_int_test.json"), SplitChange.class); + SplitChange change = IntegrationHelper.getChangeFromJsonString( + loadMockedData("splitchanges_int_test.json")); change.since = since; change.till = till; Split split = change.splits.get(0); @@ -360,7 +361,7 @@ private String getChanges(String treatment, long since, long till) { partition.size = 0; } } - return Json.toJson(change); + return Json.toJson(TargetingRulesChange.create(change)); } private void loadChanges() { diff --git a/src/main/java/io/split/android/client/service/splits/SplitsSyncHelper.java b/src/main/java/io/split/android/client/service/splits/SplitsSyncHelper.java index 5b916b89e..34ee92989 100644 --- a/src/main/java/io/split/android/client/service/splits/SplitsSyncHelper.java +++ b/src/main/java/io/split/android/client/service/splits/SplitsSyncHelper.java @@ -154,7 +154,8 @@ private long fetchUntil(long till, boolean clearBeforeUpdate, boolean avoidCache return changeNumber; } - SplitChange splitChange = fetchSplits(changeNumber, avoidCache, withCdnByPass).getFeatureFlagsChange(); // TODO + TargetingRulesChange targetingRulesChange = fetchSplits(changeNumber, avoidCache, withCdnByPass); + SplitChange splitChange = targetingRulesChange.getFeatureFlagsChange(); // TODO updateStorage(shouldClearBeforeUpdate, splitChange); shouldClearBeforeUpdate = false; From 7ffc6267e50b3c89d8046d72824d78a759aa6dda Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Wed, 5 Mar 2025 15:29:45 -0300 Subject: [PATCH 25/26] Tests migration continued --- src/androidTest/java/helper/IntegrationHelper.java | 4 ---- .../java/tests/integration/MySegmentUpdatedTest.java | 7 ++++--- .../tests/integration/SplitChangesCdnBypassTest.java | 10 +++++----- .../integration/SplitChangesServerErrorTest.java | 2 +- .../java/tests/integration/SplitChangesTest.java | 5 +++-- .../integration/SplitsTwoDifferentApiKeyTest.java | 3 ++- .../integration/shared/InterdependentSplitsTest.java | 5 ++++- .../shared/MySegmentsBeforeSplitsTest.java | 5 ++++- .../tests/integration/streaming/ControlTest.java | 2 +- .../integration/streaming/ImpressionsCountTest.java | 3 ++- .../streaming/SdkUpdateStreamingTest.java | 3 ++- .../integration/streaming/SplitsKillProcessTest.java | 3 ++- .../integration/streaming/SplitsSyncProcessTest.java | 3 ++- .../userconsent/UserConsentModeDebugTest.kt | 5 +++-- .../userconsent/UserConsentModeNoneTest.kt | 5 +++-- .../userconsent/UserConsentModeOptimizedTest.kt | 5 +++-- .../android/client/dtos/RuleBasedSegmentChange.java | 12 ++++++++++++ 17 files changed, 53 insertions(+), 29 deletions(-) diff --git a/src/androidTest/java/helper/IntegrationHelper.java b/src/androidTest/java/helper/IntegrationHelper.java index ba3b7f064..7e925c4f5 100644 --- a/src/androidTest/java/helper/IntegrationHelper.java +++ b/src/androidTest/java/helper/IntegrationHelper.java @@ -428,10 +428,6 @@ public static SplitChange getChangeFromJsonString(String json) { return Json.fromJson(json, TargetingRulesChange.class).getFeatureFlagsChange(); } - public static String splitChangesJsonToTargetingRulesChangeJson(String change) { - return Json.toJson(TargetingRulesChange.create(Json.fromJson(change, SplitChange.class))); - } - /** * A simple interface to allow us to define the response for a given path */ diff --git a/src/androidTest/java/tests/integration/MySegmentUpdatedTest.java b/src/androidTest/java/tests/integration/MySegmentUpdatedTest.java index 59bc105df..e0666789b 100644 --- a/src/androidTest/java/tests/integration/MySegmentUpdatedTest.java +++ b/src/androidTest/java/tests/integration/MySegmentUpdatedTest.java @@ -37,6 +37,7 @@ import io.split.android.client.dtos.Partition; import io.split.android.client.dtos.Split; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.dtos.TestImpressions; import io.split.android.client.dtos.UserDefinedSegmentMatcherData; import io.split.android.client.events.SplitEvent; @@ -115,10 +116,10 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio isFirstChangesReq = false; String change = mJsonChanges.get(0); return new MockResponse().setResponseCode(200) - .setBody(IntegrationHelper.splitChangesJsonToTargetingRulesChangeJson(change)); + .setBody(change); } return new MockResponse().setResponseCode(200) - .setBody(IntegrationHelper.splitChangesJsonToTargetingRulesChangeJson(emptyChanges())); + .setBody(emptyChanges()); } else if (request.getPath().contains("/testImpressions/bulk")) { @@ -239,7 +240,7 @@ private void loadSplitChanges() { split.conditions.add(0, inSegmentOneCondition); split.conditions.add(0, inSegmentTwoCondition); - mJsonChanges.add(Json.toJson(change)); + mJsonChanges.add(Json.toJson(TargetingRulesChange.create(change))); } private Condition inSegmentCondition(String name) { diff --git a/src/androidTest/java/tests/integration/SplitChangesCdnBypassTest.java b/src/androidTest/java/tests/integration/SplitChangesCdnBypassTest.java index 76d09accc..c99eff889 100644 --- a/src/androidTest/java/tests/integration/SplitChangesCdnBypassTest.java +++ b/src/androidTest/java/tests/integration/SplitChangesCdnBypassTest.java @@ -91,7 +91,7 @@ public void tillParameterIsEventuallyAdded() throws Exception { TestingHelper.delay(500); pushSplitsUpdateMessage(); - boolean await = mBypassLatch.await(200, TimeUnit.SECONDS); + boolean await = mBypassLatch.await(20, TimeUnit.SECONDS); assertTrue(await); client.destroy(); @@ -121,14 +121,14 @@ public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) { } else if (uri.getPath().contains("/splitChanges")) { System.out.println("URL HIT: " + uri.getPath()); - if (uri.getQuery().contains("till=3") && uri.getQuery().contains("since=3")) { - return getSplitsMockResponse("3", "4"); - } - if (uri.getQuery().contains("till")) { mBypassLatch.countDown(); } + if (uri.getQuery().contains("till=3") && uri.getQuery().contains("since=3")) { + return getSplitsMockResponse("3", "4"); + } + if (uri.getQuery().contains("since=-1")) { return getSplitsMockResponse("-1", "2"); } else if (uri.getQuery().contains("since=2")) { diff --git a/src/androidTest/java/tests/integration/SplitChangesServerErrorTest.java b/src/androidTest/java/tests/integration/SplitChangesServerErrorTest.java index 28fe7285f..1dea774a7 100644 --- a/src/androidTest/java/tests/integration/SplitChangesServerErrorTest.java +++ b/src/androidTest/java/tests/integration/SplitChangesServerErrorTest.java @@ -191,7 +191,7 @@ private void loadSplitChanges() { p1.size = (i < 2 ? 100 : 0); p2.treatment = "off"; p2.size = (i < 2 ? 0 : 100); - mJsonChanges.add(Json.toJson(change)); + mJsonChanges.add(Json.toJson(TargetingRulesChange.create(change))); } } diff --git a/src/androidTest/java/tests/integration/SplitChangesTest.java b/src/androidTest/java/tests/integration/SplitChangesTest.java index f399aeb98..89243574c 100644 --- a/src/androidTest/java/tests/integration/SplitChangesTest.java +++ b/src/androidTest/java/tests/integration/SplitChangesTest.java @@ -31,6 +31,7 @@ import io.split.android.client.dtos.Partition; import io.split.android.client.dtos.Split; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.dtos.TestImpressions; import io.split.android.client.events.SplitEvent; import io.split.android.client.impressions.Impression; @@ -94,7 +95,7 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio mLatchs.get(currReq - 1).countDown(); } String changes = mJsonChanges.get(currReq); - return new MockResponse().setResponseCode(200).setBody(IntegrationHelper.splitChangesJsonToTargetingRulesChangeJson(changes)); + return new MockResponse().setResponseCode(200).setBody(changes); } else if (currReq == mLatchs.size()) { mLatchs.get(currReq - 1).countDown(); } @@ -223,7 +224,7 @@ private void loadSplitChanges() { p1.size = (even ? 100 : 0); p2.treatment = "off_" + i; p2.size = (even ? 0 : 100); - mJsonChanges.add(Json.toJson(change)); + mJsonChanges.add(Json.toJson(TargetingRulesChange.create(change))); } } diff --git a/src/androidTest/java/tests/integration/SplitsTwoDifferentApiKeyTest.java b/src/androidTest/java/tests/integration/SplitsTwoDifferentApiKeyTest.java index 2556d2f27..c8b04b3ec 100644 --- a/src/androidTest/java/tests/integration/SplitsTwoDifferentApiKeyTest.java +++ b/src/androidTest/java/tests/integration/SplitsTwoDifferentApiKeyTest.java @@ -34,6 +34,7 @@ import io.split.android.client.SplitFactory; import io.split.android.client.TestingConfig; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.events.SplitEvent; import io.split.android.client.network.HttpMethod; import io.split.android.client.storage.db.SplitRoomDatabase; @@ -232,7 +233,7 @@ private String getSplitChanges(int factoryNumber, int hitNumber) { mSplitChange.splits.get(0).name = "split" + factoryNumber; mSplitChange.since = (hitNumber == 0 ? -1 : CHANGE_NUMBER_BASE + factoryNumber); mSplitChange.till = CHANGE_NUMBER_BASE + factoryNumber; - return Json.toJson(mSplitChange); + return Json.toJson(TargetingRulesChange.create(mSplitChange)); } private void testSplitsUpdate(long changeNumber, int factoryNumber) throws InterruptedException { diff --git a/src/androidTest/java/tests/integration/shared/InterdependentSplitsTest.java b/src/androidTest/java/tests/integration/shared/InterdependentSplitsTest.java index 65167f866..c899a446c 100644 --- a/src/androidTest/java/tests/integration/shared/InterdependentSplitsTest.java +++ b/src/androidTest/java/tests/integration/shared/InterdependentSplitsTest.java @@ -11,7 +11,10 @@ import helper.IntegrationHelper; import io.split.android.client.SplitClient; import io.split.android.client.api.Key; +import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.events.SplitEvent; +import io.split.android.client.utils.Json; import okhttp3.mockwebserver.Dispatcher; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.RecordedRequest; @@ -40,7 +43,7 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio String splitChange = "{\"splits\":[{\"trafficTypeName\":\"account\",\"name\":\"android_test_2\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-1955610140,\"seed\":-633015570,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1648733409158,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"IN_SPLIT_TREATMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":{\"split\":\"android_test_3\",\"treatments\":[\"on\"]},\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0}],\"label\":\"in split android_test_3 treatment [on]\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"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\":\"account\",\"name\":\"android_test_3\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-397942789,\"seed\":1852089605,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1648733496087,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"IN_SEGMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":{\"segmentName\":\"android_test\"},\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0}],\"label\":\"in segment android_test\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"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\":1648733409158,\"till\":1648733409158}"; return new MockResponse().setResponseCode(200) - .setBody(splitChange); + .setBody(Json.toJson(TargetingRulesChange.create(Json.fromJson(splitChange, SplitChange.class)))); } else if (request.getPath().contains("/events/bulk")) { return new MockResponse().setResponseCode(200); } else { diff --git a/src/androidTest/java/tests/integration/shared/MySegmentsBeforeSplitsTest.java b/src/androidTest/java/tests/integration/shared/MySegmentsBeforeSplitsTest.java index 7b5423f8d..9c55e1656 100644 --- a/src/androidTest/java/tests/integration/shared/MySegmentsBeforeSplitsTest.java +++ b/src/androidTest/java/tests/integration/shared/MySegmentsBeforeSplitsTest.java @@ -10,7 +10,10 @@ import helper.IntegrationHelper; import io.split.android.client.SplitClient; import io.split.android.client.api.Key; +import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.events.SplitEvent; +import io.split.android.client.utils.Json; import okhttp3.mockwebserver.Dispatcher; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.RecordedRequest; @@ -34,7 +37,7 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio String splitChange = "{\"splits\":[{\"trafficTypeName\":\"account\",\"name\":\"android_test_2\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-1955610140,\"seed\":-633015570,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1648733409158,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"IN_SPLIT_TREATMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":{\"split\":\"android_test_3\",\"treatments\":[\"on\"]},\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0}],\"label\":\"in split android_test_3 treatment [on]\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"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\":\"account\",\"name\":\"android_test_3\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-397942789,\"seed\":1852089605,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1648733496087,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"IN_SEGMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":{\"segmentName\":\"android_test\"},\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0}],\"label\":\"in segment android_test\"}]}],\"since\":1648733409158,\"till\":1648733409158}"; new CountDownLatch(1).await(500, TimeUnit.MILLISECONDS); return new MockResponse().setResponseCode(200) - .setBody(splitChange); + .setBody(Json.toJson(TargetingRulesChange.create(Json.fromJson(splitChange, SplitChange.class)))); } else if (request.getPath().contains("/events/bulk")) { return new MockResponse().setResponseCode(200); } else { diff --git a/src/androidTest/java/tests/integration/streaming/ControlTest.java b/src/androidTest/java/tests/integration/streaming/ControlTest.java index 27055c780..9be4dd28f 100644 --- a/src/androidTest/java/tests/integration/streaming/ControlTest.java +++ b/src/androidTest/java/tests/integration/streaming/ControlTest.java @@ -274,7 +274,7 @@ private String loadMockedData(String fileName) { private String loadSplitChanges() { String jsonChange = new FileHelper().loadFileContent(mContext, "simple_split.json"); - SplitChange change = Json.fromJson(jsonChange, SplitChange.class); + SplitChange change = Json.fromJson(jsonChange, TargetingRulesChange.class).getFeatureFlagsChange(); change.since = 500; change.till = 500; return Json.toJson(TargetingRulesChange.create(change)); diff --git a/src/androidTest/java/tests/integration/streaming/ImpressionsCountTest.java b/src/androidTest/java/tests/integration/streaming/ImpressionsCountTest.java index d64bff001..ad01c035b 100644 --- a/src/androidTest/java/tests/integration/streaming/ImpressionsCountTest.java +++ b/src/androidTest/java/tests/integration/streaming/ImpressionsCountTest.java @@ -37,6 +37,7 @@ import io.split.android.client.dtos.KeyImpression; import io.split.android.client.dtos.Split; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.dtos.TestImpressions; import io.split.android.client.events.SplitEvent; import io.split.android.client.network.HttpMethod; @@ -249,7 +250,7 @@ private void loadSplitChanges() { private String getSplitChanges() { mSplitChange.splits.get(0).changeNumber = CHANGE_NUMBER; mSplitChange.till = CHANGE_NUMBER; - return Json.toJson(mSplitChange); + return Json.toJson(TargetingRulesChange.create(mSplitChange)); } private String loadMockedData(String fileName) { diff --git a/src/androidTest/java/tests/integration/streaming/SdkUpdateStreamingTest.java b/src/androidTest/java/tests/integration/streaming/SdkUpdateStreamingTest.java index 55ce985a8..6985d990c 100644 --- a/src/androidTest/java/tests/integration/streaming/SdkUpdateStreamingTest.java +++ b/src/androidTest/java/tests/integration/streaming/SdkUpdateStreamingTest.java @@ -35,6 +35,7 @@ import io.split.android.client.dtos.Partition; import io.split.android.client.dtos.Split; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.events.SplitEvent; import io.split.android.client.network.HttpMethod; import io.split.android.client.storage.db.GeneralInfoEntity; @@ -420,7 +421,7 @@ private String getChanges(String treatment, long since, long till) { partition.size = 0; } } - return Json.toJson(change); + return Json.toJson(TargetingRulesChange.create(change)); } private void loadChanges() { diff --git a/src/androidTest/java/tests/integration/streaming/SplitsKillProcessTest.java b/src/androidTest/java/tests/integration/streaming/SplitsKillProcessTest.java index d884324f8..f1d8c4157 100644 --- a/src/androidTest/java/tests/integration/streaming/SplitsKillProcessTest.java +++ b/src/androidTest/java/tests/integration/streaming/SplitsKillProcessTest.java @@ -35,6 +35,7 @@ import io.split.android.client.api.Key; import io.split.android.client.dtos.Split; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.events.SplitEvent; import io.split.android.client.network.HttpMethod; import io.split.android.client.storage.db.GeneralInfoEntity; @@ -243,7 +244,7 @@ private String getSplitChanges(int hit) { split.defaultTreatment = "off"; mSplitChange.since = CHANGE_NUMBER; mSplitChange.till = CHANGE_NUMBER; - return Json.toJson(mSplitChange); + return Json.toJson(TargetingRulesChange.create(mSplitChange)); } private Split parseEntity(SplitEntity entity) { diff --git a/src/androidTest/java/tests/integration/streaming/SplitsSyncProcessTest.java b/src/androidTest/java/tests/integration/streaming/SplitsSyncProcessTest.java index f5eae5dc1..d4773eb06 100644 --- a/src/androidTest/java/tests/integration/streaming/SplitsSyncProcessTest.java +++ b/src/androidTest/java/tests/integration/streaming/SplitsSyncProcessTest.java @@ -34,6 +34,7 @@ import io.split.android.client.api.Key; import io.split.android.client.dtos.Split; import io.split.android.client.dtos.SplitChange; +import io.split.android.client.dtos.TargetingRulesChange; import io.split.android.client.events.SplitEvent; import io.split.android.client.network.HttpMethod; import io.split.android.client.storage.db.GeneralInfoEntity; @@ -222,7 +223,7 @@ private String getSplitChanges(int hit) { mSplitChange.splits.get(0).changeNumber = CHANGE_NUMBER; mSplitChange.since = CHANGE_NUMBER; mSplitChange.till = CHANGE_NUMBER; - return Json.toJson(mSplitChange); + return Json.toJson(TargetingRulesChange.create(mSplitChange)); } private Split parseEntity(SplitEntity entity) { diff --git a/src/androidTest/java/tests/integration/userconsent/UserConsentModeDebugTest.kt b/src/androidTest/java/tests/integration/userconsent/UserConsentModeDebugTest.kt index 16b224947..46017e3e1 100644 --- a/src/androidTest/java/tests/integration/userconsent/UserConsentModeDebugTest.kt +++ b/src/androidTest/java/tests/integration/userconsent/UserConsentModeDebugTest.kt @@ -7,6 +7,7 @@ import helper.* import io.split.android.client.SplitClient import io.split.android.client.SplitFactory import io.split.android.client.dtos.SplitChange +import io.split.android.client.dtos.TargetingRulesChange import io.split.android.client.events.SplitEvent import io.split.android.client.events.SplitEventTask import io.split.android.client.network.HttpMethod @@ -267,8 +268,8 @@ class UserConsentModeDebugTest { private fun loadSplitChanges(): String? { val fileHelper = FileHelper() val change = fileHelper.loadFileContent(mContext, "split_changes_1.json") - val parsedChange = Json.fromJson(change, SplitChange::class.java) + val parsedChange = Json.fromJson(change, TargetingRulesChange::class.java).featureFlagsChange parsedChange.since = parsedChange.till - return Json.toJson(parsedChange) + return Json.toJson(TargetingRulesChange.create(parsedChange)) } } diff --git a/src/androidTest/java/tests/integration/userconsent/UserConsentModeNoneTest.kt b/src/androidTest/java/tests/integration/userconsent/UserConsentModeNoneTest.kt index fd8e7b4cc..6a509f422 100644 --- a/src/androidTest/java/tests/integration/userconsent/UserConsentModeNoneTest.kt +++ b/src/androidTest/java/tests/integration/userconsent/UserConsentModeNoneTest.kt @@ -7,6 +7,7 @@ import helper.* import io.split.android.client.SplitClient import io.split.android.client.SplitFactory import io.split.android.client.dtos.SplitChange +import io.split.android.client.dtos.TargetingRulesChange import io.split.android.client.events.SplitEvent import io.split.android.client.events.SplitEventTask import io.split.android.client.network.HttpMethod @@ -274,8 +275,8 @@ class UserConsentModeNoneTest { private fun loadSplitChanges(): String? { val fileHelper = FileHelper() val change = fileHelper.loadFileContent(mContext, "split_changes_1.json") - val parsedChange = Json.fromJson(change, SplitChange::class.java) + val parsedChange = Json.fromJson(change, TargetingRulesChange::class.java).featureFlagsChange parsedChange.since = parsedChange.till - return Json.toJson(parsedChange) + return Json.toJson(TargetingRulesChange.create(parsedChange)) } } diff --git a/src/androidTest/java/tests/integration/userconsent/UserConsentModeOptimizedTest.kt b/src/androidTest/java/tests/integration/userconsent/UserConsentModeOptimizedTest.kt index ea7580626..210b5ccf3 100644 --- a/src/androidTest/java/tests/integration/userconsent/UserConsentModeOptimizedTest.kt +++ b/src/androidTest/java/tests/integration/userconsent/UserConsentModeOptimizedTest.kt @@ -7,6 +7,7 @@ import helper.* import io.split.android.client.SplitClient import io.split.android.client.SplitFactory import io.split.android.client.dtos.SplitChange +import io.split.android.client.dtos.TargetingRulesChange import io.split.android.client.events.SplitEvent import io.split.android.client.events.SplitEventTask import io.split.android.client.network.HttpMethod @@ -279,8 +280,8 @@ class UserConsentModeOptimizedTest { private fun loadSplitChanges(): String? { val fileHelper = FileHelper() val change = fileHelper.loadFileContent(mContext, "split_changes_1.json") - val parsedChange = Json.fromJson(change, SplitChange::class.java) + val parsedChange = Json.fromJson(change, TargetingRulesChange::class.java).featureFlagsChange parsedChange.since = parsedChange.till - return Json.toJson(parsedChange) + return Json.toJson(TargetingRulesChange.create(parsedChange)) } } diff --git a/src/main/java/io/split/android/client/dtos/RuleBasedSegmentChange.java b/src/main/java/io/split/android/client/dtos/RuleBasedSegmentChange.java index 712d9f109..8d99b2f21 100644 --- a/src/main/java/io/split/android/client/dtos/RuleBasedSegmentChange.java +++ b/src/main/java/io/split/android/client/dtos/RuleBasedSegmentChange.java @@ -1,7 +1,10 @@ package io.split.android.client.dtos; +import androidx.annotation.VisibleForTesting; + import com.google.gson.annotations.SerializedName; +import java.util.ArrayList; import java.util.List; public class RuleBasedSegmentChange { @@ -26,4 +29,13 @@ public long getTill() { public List getSegments() { return segments; } + + @VisibleForTesting + public static RuleBasedSegmentChange createEmpty() { + RuleBasedSegmentChange ruleBasedSegmentChange = new RuleBasedSegmentChange(); + ruleBasedSegmentChange.segments = new ArrayList<>(); + ruleBasedSegmentChange.since = -1; + ruleBasedSegmentChange.till = -1; + return ruleBasedSegmentChange; + } } From c0c380dc93a7613e2ff01918f6cf08a701db649d Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Wed, 5 Mar 2025 16:28:58 -0300 Subject: [PATCH 26/26] Fix --- .../tests/integration/streaming/SplitsKillProcessTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/androidTest/java/tests/integration/streaming/SplitsKillProcessTest.java b/src/androidTest/java/tests/integration/streaming/SplitsKillProcessTest.java index f1d8c4157..343e852bb 100644 --- a/src/androidTest/java/tests/integration/streaming/SplitsKillProcessTest.java +++ b/src/androidTest/java/tests/integration/streaming/SplitsKillProcessTest.java @@ -222,7 +222,8 @@ private void pushMessage(String fileName) { } private void loadSplitChanges() { - SplitChange change = IntegrationHelper.getChangeFromJsonString("splitchanges_int_test.json"); + SplitChange change = IntegrationHelper.getChangeFromJsonString( + loadMockedData("splitchanges_int_test.json")); Split split = change.splits.get(0); split.name = "test_feature_1";