Skip to content

Commit

Permalink
Sets validator (#523)
Browse files Browse the repository at this point in the history
  • Loading branch information
gthea authored Aug 29, 2023
2 parents a896f6d + 35f6e99 commit 473ebad
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 7 deletions.
28 changes: 21 additions & 7 deletions src/main/java/io/split/android/client/SplitFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,29 @@
import androidx.annotation.NonNull;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import io.split.android.client.validators.FlagSetsValidatorImpl;
import io.split.android.client.validators.SplitFilterValidator;

public class SplitFilter {
public enum Type {
// Filters here has to be defined in the order
// it will be in querystring
BY_NAME,
BY_SET,
BY_PREFIX;

@NonNull
@Override
public String toString() {
switch (this) {
case BY_NAME:
return "by split name";
case BY_PREFIX:
return "by split prefix";
case BY_SET:
return "by flag set";
default:
return "Invalid type";
}
Expand All @@ -31,6 +37,8 @@ public String queryStringField() {
return "names";
case BY_PREFIX:
return "prefixes";
case BY_SET:
return "sets";
default:
return "unknown";
}
Expand All @@ -42,6 +50,8 @@ public int maxValuesCount() {
return 400;
case BY_PREFIX:
return 50;
case BY_SET:
return 1000;
default:
return 0;
}
Expand All @@ -59,26 +69,30 @@ static public SplitFilter byPrefix(@NonNull List<String> values) {
return new SplitFilter(Type.BY_PREFIX, values);
}

static public SplitFilter bySet(@NonNull List<String> values) {
return new SplitFilter(Type.BY_SET, values, new FlagSetsValidatorImpl());
}

// This constructor is not private (but default) to allow Split Sync Config builder be agnostic when creating filters
// Also is not public to force SDK users to use static functions "byName" and "byPrefix"
SplitFilter(Type type, List<String> values) {
if(values == null) {
if (values == null) {
throw new IllegalArgumentException("Values can't be null for " + type.toString() + " filter");
}
mType = type;
mValues = new ArrayList<>(values);
}

SplitFilter(Type type, List<String> values, SplitFilterValidator validator) {
mType = type;
mValues = validator.cleanup(values);
}

public Type getType() {
return mType;
}

public List<String> getValues() {
return mValues;
}

public void updateValues(List<String> values) {
mValues.clear();
mValues.addAll(values);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package io.split.android.client.validators;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.TreeSet;

import io.split.android.client.utils.logger.Logger;

public class FlagSetsValidatorImpl implements SplitFilterValidator {

private static final String FLAG_SET_REGEX = "^[a-z][_a-z0-9]{0,49}$";

/**
* Validates the flag sets and returns a list of
* de-duplicated and alphanumerically ordered valid flag sets.
*
* @param values list of flag sets
* @return list of unique alphanumerically ordered valid flag sets
*/
@Override
public List<String> cleanup(List<String> values) {
if (values == null || values.isEmpty()) {
return Collections.emptyList();
}

TreeSet<String> cleanedUpSets = new TreeSet<>();
for (String set : values) {
if (set == null || set.isEmpty()) {
continue;
}

if (set.startsWith(" ") || set.endsWith(" ")) {
Logger.w("SDK config: Flag Set name " + set + " has extra whitespace, trimming");
set = set.trim();
}

if (set.matches(FLAG_SET_REGEX)) {
cleanedUpSets.add(set);
} else {
Logger.w("SDK config: you passed "+ set +", Flag Set must adhere to the regular expressions "+ FLAG_SET_REGEX +". This means a Flag Set must be start with a letter, be in lowercase, alphanumeric and have a max length of 50 characters. "+ set +" was discarded.");
}
}

return new ArrayList<>(cleanedUpSets);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.split.android.client.validators;

import java.util.List;

public interface SplitFilterValidator {

List<String> cleanup(List<String> values);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package io.split.android.client.validators;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import org.junit.Test;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class FlagSetsValidatorImplTest {

private final FlagSetsValidatorImpl mValidator = new FlagSetsValidatorImpl();

@Test
public void nullInputReturnsEmptyList() {
List<String> result = mValidator.cleanup(null);
assertTrue(result.isEmpty());
}

@Test
public void emptyInputReturnsEmptyList() {
List<String> result = mValidator.cleanup(Collections.emptyList());
assertTrue(result.isEmpty());
}

@Test
public void duplicatedInputValuesAreRemoved() {
List<String> result = mValidator.cleanup(Arrays.asList("set1", "set1"));
assertEquals(1, result.size());
assertTrue(result.contains("set1"));
}

@Test
public void valuesAreSortedAlphanumerically() {
List<String> result = mValidator.cleanup(Arrays.asList("set2", "set1", "set_1"));
assertEquals(3, result.size());
assertEquals("set1", result.get(0));
assertEquals("set2", result.get(1));
assertEquals("set_1", result.get(2));
}

@Test
public void invalidValuesAreRemoved() {
List<String> result = mValidator.cleanup(Arrays.asList("set1", "set2", "set_1", "set-1", "set 1", "set 2"));
assertEquals(3, result.size());
assertEquals("set1", result.get(0));
assertEquals("set2", result.get(1));
assertEquals("set_1", result.get(2));
}

@Test
public void setWithMoreThan50CharsIsRemoved() {
String longSet = "abcdfghijklmnopqrstuvwxyz1234567890abcdfghijklmnopq";
List<String> result = mValidator.cleanup(Arrays.asList("set1", longSet));
assertEquals(51, longSet.length());
assertEquals(1, result.size());
assertEquals("set1", result.get(0));
}

@Test
public void setWithLessThanOneCharIsOrEmptyRemoved() {
List<String> result = mValidator.cleanup(Arrays.asList("set1", "", " "));
assertEquals(1, result.size());
assertEquals("set1", result.get(0));
}

@Test
public void nullSetIsRemoved() {
List<String> result = mValidator.cleanup(Arrays.asList("set1", null));
assertEquals(1, result.size());
assertEquals("set1", result.get(0));
}

@Test
public void setWithExtraWhitespaceIsTrimmed() {
List<String> result = mValidator.cleanup(Arrays.asList("set1 ", " set2", "set3", "set 4"));
assertEquals(3, result.size());
assertEquals("set1", result.get(0));
assertEquals("set2", result.get(1));
assertEquals("set3", result.get(2));
}
}

0 comments on commit 473ebad

Please sign in to comment.