Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ on:
- 'development'
- '*_baseline'

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build-app:
name: Build App
Expand All @@ -15,16 +19,16 @@ jobs:
ARTIFACTORY_TOKEN: ${{ secrets.ARTIFACTORY_TOKEN }}
steps:
- name: checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Gradle cache
uses: gradle/gradle-build-action@v2.4.2
uses: gradle/gradle-build-action@v3

- name: Set up JDK 11
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: 11
java-version: 17
cache: 'gradle'

- name: Publish
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/instrumented.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ on:
branches:
- 'development'
- 'master'
- '*_baseline'

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
Expand Down
10 changes: 7 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,26 @@ on:
branches:
- '*'

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build-app:
name: Build App
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Gradle cache
uses: gradle/gradle-build-action@v3

- name: Set up JDK 11
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 11
java-version: 17
cache: 'gradle'

- name: Test with Gradle
Expand Down

This file was deleted.

11 changes: 6 additions & 5 deletions src/main/java/io/split/android/client/EventsTrackerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import io.split.android.client.telemetry.storage.TelemetryStorageProducer;
import io.split.android.client.utils.logger.Logger;
import io.split.android.client.validators.EventValidator;
import io.split.android.client.validators.PropertyValidator;
import io.split.android.client.validators.ValidationErrorInfo;
import io.split.android.client.validators.ValidationMessageLogger;

Expand All @@ -23,20 +24,20 @@ public class EventsTrackerImpl implements EventsTracker {
private final EventValidator mEventValidator;
private final ValidationMessageLogger mValidationLogger;
private final TelemetryStorageProducer mTelemetryStorageProducer;
private final EventPropertiesProcessor mEventPropertiesProcessor;
private final PropertyValidator mPropertyValidator;
private final SyncManager mSyncManager;
private final AtomicBoolean isTrackingEnabled = new AtomicBoolean(true);

public EventsTrackerImpl(@NonNull EventValidator eventValidator,
@NonNull ValidationMessageLogger validationLogger,
@NonNull TelemetryStorageProducer telemetryStorageProducer,
@NonNull EventPropertiesProcessor eventPropertiesProcessor,
@NonNull PropertyValidator eventPropertiesProcessor,
@NonNull SyncManager syncManager) {

mEventValidator = checkNotNull(eventValidator);
mValidationLogger = checkNotNull(validationLogger);
mTelemetryStorageProducer = checkNotNull(telemetryStorageProducer);
mEventPropertiesProcessor = checkNotNull(eventPropertiesProcessor);
mPropertyValidator = checkNotNull(eventPropertiesProcessor);
mSyncManager = checkNotNull(syncManager);
}

Expand Down Expand Up @@ -74,8 +75,8 @@ public boolean track(String key, String trafficType, String eventType,
event.trafficTypeName = event.trafficTypeName.toLowerCase();
}

ProcessedEventProperties processedProperties =
mEventPropertiesProcessor.process(event.properties);
PropertyValidator.Result processedProperties =
mPropertyValidator.validate(event.properties, validationTag);
if (!processedProperties.isValid()) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,62 +4,60 @@
import java.util.Map;

import io.split.android.client.utils.logger.Logger;
import io.split.android.client.validators.PropertyValidator;
import io.split.android.client.validators.ValidationConfig;


public class EventPropertiesProcessorImpl implements EventPropertiesProcessor {
public class PropertyValidatorImpl implements PropertyValidator {

private static final String VALIDATION_TAG = "track";
private final static int MAX_PROPS_COUNT = 300;
private final static int MAXIMUM_EVENT_PROPERTY_BYTES =
ValidationConfig.getInstance().getMaximumEventPropertyBytes();

@Override
public ProcessedEventProperties process(Map<String, Object> properties) {
public Result validate(Map<String, Object> properties, String validationTag) {
if (properties == null) {
return new ProcessedEventProperties(true, null, 0);
return Result.valid(null, 0);
}

if (properties.size() > MAX_PROPS_COUNT) {
Logger.w(VALIDATION_TAG + "Event has more than " + MAX_PROPS_COUNT +
Logger.w(validationTag + "Event has more than " + MAX_PROPS_COUNT +
" properties. Some of them will be trimmed when processed");
}
int sizeInBytes = 0;
Map<String, Object> finalProperties = new HashMap<>(properties);

for (Map.Entry entry : properties.entrySet()) {
for (Map.Entry<String, Object> entry : properties.entrySet()) {
Object value = entry.getValue();
String key = entry.getKey().toString();
String key = entry.getKey();

if (value != null && isInvalidValueType(value)) {
finalProperties.put(key, null);
}
sizeInBytes += calculateEventSizeInBytes(key, value);

if (sizeInBytes > MAXIMUM_EVENT_PROPERTY_BYTES) {
Logger.w(VALIDATION_TAG +
Logger.w(validationTag +
"The maximum size allowed for the " +
" properties is 32kb. Current is " + key +
". Event not queued");
return ProcessedEventProperties.InvalidProperties();
return Result.invalid("Event properties size is too large", sizeInBytes);
}
}
return new ProcessedEventProperties(true, finalProperties, sizeInBytes);
return Result.valid(finalProperties, sizeInBytes);
}

private boolean isInvalidValueType(Object value) {
private static boolean isInvalidValueType(Object value) {
return !(value instanceof Number) &&
!(value instanceof Boolean) &&
!(value instanceof String);
}

private int calculateEventSizeInBytes(String key, Object value) {
private static int calculateEventSizeInBytes(String key, Object value) {
int valueSize = 0;
if(value != null && value.getClass() == String.class) {
valueSize = value.toString().getBytes().length;
}
return valueSize + key.getBytes().length;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ public EventsTracker getEventsTracker() {
if (mEventsTracker == null) {
EventValidator eventsValidator = new EventValidatorImpl(new KeyValidatorImpl(), mSplitsStorage);
mEventsTracker = new EventsTrackerImpl(eventsValidator, new ValidationMessageLoggerImpl(), mTelemetryStorage,
new EventPropertiesProcessorImpl(), mSyncManager);
new PropertyValidatorImpl(), mSyncManager);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import io.split.android.client.EvaluationOptions;
import io.split.android.client.EvaluatorImpl;
import io.split.android.client.FlagSetsFilter;
import io.split.android.client.PropertyValidatorImpl;
import io.split.android.client.SplitClient;
import io.split.android.client.SplitClientConfig;
import io.split.android.client.SplitFactory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@

public interface PropertyValidator {

Result validate(Map<String, Object> properties);

Result validate(Map<String, Object> properties, int initialSizeInBytes, String validationTag);
Result validate(Map<String, Object> properties, String validationTag);

class Result {

Expand All @@ -20,7 +18,7 @@ class Result {
@Nullable
private final String mErrorMessage;

private Result(boolean isValid, Map<String, Object> properties, int sizeInBytes, String errorMessage) {
private Result(boolean isValid, @Nullable Map<String, Object> properties, int sizeInBytes, @Nullable String errorMessage) {
mIsValid = isValid;
mValidatedProperties = properties;
mSizeInBytes = sizeInBytes;
Expand All @@ -32,7 +30,7 @@ public boolean isValid() {
}

@Nullable
public Map<String, Object> getValidatedProperties() {
public Map<String, Object> getProperties() {
return mValidatedProperties;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import io.split.android.client.Evaluator;
import io.split.android.client.EvaluatorImpl;
import io.split.android.client.FlagSetsFilter;
import io.split.android.client.PropertyValidatorImpl;
import io.split.android.client.api.Key;
import io.split.android.client.attributes.AttributesManager;
import io.split.android.client.attributes.AttributesMerger;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -354,19 +354,19 @@ private String serializeProperties(@Nullable EvaluationOptions evaluationOptions
}

// validate using property validator
PropertyValidator.Result result = mPropertyValidator.validate(evaluationOptions.getProperties());
PropertyValidator.Result result = mPropertyValidator.validate(evaluationOptions.getProperties(), validationTag);

if (!result.isValid()) {
mValidationLogger.e("Properties validation failed: " + (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error"), validationTag);
return null;
}

if (result.getValidatedProperties() == null || result.getValidatedProperties().isEmpty()) {
if (result.getProperties() == null || result.getProperties().isEmpty()) {
return null;
}

try {
return Json.toJson(result.getValidatedProperties());
return Json.toJson(result.getProperties());
} catch (Exception e) {
mValidationLogger.e("Failed to serialize properties to JSON: " + e.getLocalizedMessage(), validationTag);
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public void setUp() {
public void evaluationWithValidPropertiesAddsThemToImpressionAsJsonString() {
when(mEvaluator.getTreatment(anyString(), anyString(), anyString(), anyMap())).thenReturn(new EvaluationResult("test", "label"));
EvaluationOptions evaluationOptions = getEvaluationOptions();
when(mPropertyValidator.validate(any())).thenReturn(PropertyValidator.Result.valid(evaluationOptions.getProperties(), 0));
when(mPropertyValidator.validate(any(), any())).thenReturn(PropertyValidator.Result.valid(evaluationOptions.getProperties(), 0));

mTreatmentManager.getTreatmentWithConfig("test", null, evaluationOptions, false);

Expand All @@ -92,7 +92,7 @@ public boolean matches(Impression argument) {
@Test
public void evaluationWithEmptyPropertiesAddsNullPropertiesToImpression() {
when(mEvaluator.getTreatment(anyString(), anyString(), anyString(), anyMap())).thenReturn(new EvaluationResult("test", "label"));
when(mPropertyValidator.validate(any())).thenReturn(PropertyValidator.Result.valid(null, 0));
when(mPropertyValidator.validate(any(), any())).thenReturn(PropertyValidator.Result.valid(null, 0));

mTreatmentManager.getTreatmentWithConfig("test", null, new EvaluationOptions(new HashMap<>()), false);

Expand All @@ -108,7 +108,7 @@ public boolean matches(Impression argument) {
public void invalidPropertiesAreNotAddedToImpression() {
when(mEvaluator.getTreatment(anyString(), anyString(), anyString(), anyMap())).thenReturn(new EvaluationResult("test", "label"));
EvaluationOptions evaluationOptions = getEvaluationOptions();
when(mPropertyValidator.validate(any())).thenReturn(PropertyValidator.Result.invalid("Invalid properties", 0));
when(mPropertyValidator.validate(any(), any())).thenReturn(PropertyValidator.Result.invalid("Invalid properties", 0));

mTreatmentManager.getTreatmentWithConfig("test", null, evaluationOptions, false);

Expand All @@ -124,7 +124,7 @@ public boolean matches(Impression argument) {
public void invalidPropertiesLogsMessageInValidationMessageLogger() {
when(mEvaluator.getTreatment(anyString(), anyString(), anyString(), anyMap())).thenReturn(new EvaluationResult("test", "label"));
EvaluationOptions evaluationOptions = getEvaluationOptions();
when(mPropertyValidator.validate(any())).thenReturn(PropertyValidator.Result.invalid("Invalid properties", 0));
when(mPropertyValidator.validate(any(), any())).thenReturn(PropertyValidator.Result.invalid("Invalid properties", 0));

mTreatmentManager.getTreatmentWithConfig("test", null, evaluationOptions, false);

Expand All @@ -138,7 +138,7 @@ public void propertiesAreValidatedWithPropertyValidator() {

mTreatmentManager.getTreatmentWithConfig("test", null, evaluationOptions, false);

verify(mPropertyValidator).validate(evaluationOptions.getProperties());
verify(mPropertyValidator).validate(evaluationOptions.getProperties(), "getTreatmentWithConfig");
}

@NonNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,16 @@
import java.util.HashMap;
import java.util.Map;

import io.split.android.client.EventPropertiesProcessor;
import io.split.android.client.EventPropertiesProcessorImpl;
import io.split.android.client.ProcessedEventProperties;
import io.split.android.client.PropertyValidatorImpl;
import io.split.android.client.dtos.Split;
import io.split.android.client.utils.Utils;
import io.split.android.client.validators.PropertyValidator;
import io.split.android.client.validators.ValidationConfig;

public class EventPropertiesProcessorTest {
public class PropertyValidatorTest {

private EventPropertiesProcessor processor = new EventPropertiesProcessorImpl();
private final PropertyValidator processor = new PropertyValidatorImpl();
private final static long MAX_BYTES = ValidationConfig.getInstance().getMaximumEventPropertyBytes();
private final static int MAX_COUNT = 300;

@Before
public void setup() {
Expand All @@ -33,7 +31,7 @@ public void sizeInBytesValidation() {
properties.put("key" + count, Utils.repeat("a", 1021)); // 1025 bytes
count++;
}
ProcessedEventProperties result = processor.process(properties);
PropertyValidator.Result result = validate(properties);

Assert.assertFalse(result.isValid());
}
Expand All @@ -47,7 +45,7 @@ public void invalidPropertyType() {
for (int i = 0; i < 10; i++) {
properties.put("key" + i, new Split());
}
ProcessedEventProperties result = processor.process(properties);
PropertyValidator.Result result = validate(properties);

Assert.assertTrue(result.isValid());
Assert.assertEquals(10, result.getProperties().size());
Expand All @@ -62,7 +60,7 @@ public void nullValues() {
for (int i = 10; i < 20; i++) {
properties.put("key" + i + 10, null);
}
ProcessedEventProperties result = processor.process(properties);
PropertyValidator.Result result = validate(properties);

Assert.assertTrue(result.isValid());
Assert.assertEquals(20, result.getProperties().size());
Expand All @@ -74,9 +72,13 @@ public void totalBytes() {
for (int i = 0; i < 10; i++) {
properties.put("k" + i, "10 bytes");
}
ProcessedEventProperties result = processor.process(properties);
PropertyValidator.Result result = validate(properties);

Assert.assertTrue(result.isValid());
Assert.assertEquals(100, result.getSizeInBytes());
}

private PropertyValidator.Result validate(Map<String, Object> properties) {
return processor.validate(properties, "test");
}
}
Loading
Loading