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
24 changes: 23 additions & 1 deletion core-api/src/main/java/com/optimizely/ab/Optimizely.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@
import com.optimizely.ab.error.RaiseExceptionErrorHandler;
import com.optimizely.ab.event.EventHandler;
import com.optimizely.ab.event.LogEvent;
import com.optimizely.ab.event.internal.BuildVersionInfo;
import com.optimizely.ab.event.internal.EventBuilder;
import com.optimizely.ab.event.internal.EventBuilderV1;
import com.optimizely.ab.event.internal.EventBuilderV2;
import com.optimizely.ab.event.internal.payload.Event.ClientEngine;
import com.optimizely.ab.internal.ProjectValidationUtils;

import org.slf4j.Logger;
Expand Down Expand Up @@ -441,6 +443,8 @@ public static class Builder {
private ErrorHandler errorHandler;
private EventHandler eventHandler;
private EventBuilder eventBuilder;
private ClientEngine clientEngine;
private String clientVersion;
private ProjectConfig projectConfig;

public Builder(@Nonnull String datafile,
Expand All @@ -459,6 +463,16 @@ public Builder withUserExperimentRecord(UserExperimentRecord userExperimentRecor
return this;
}

public Builder withClientEngine(ClientEngine clientEngine) {
this.clientEngine = clientEngine;
return this;
}

public Builder withClientVersion(String clientVersion) {
this.clientVersion = clientVersion;
return this;
}

protected Builder withBucketing(Bucketer bucketer) {
this.bucketer = bucketer;
return this;
Expand All @@ -485,11 +499,19 @@ public Optimizely build() throws ConfigParseException {
bucketer = new Bucketer(projectConfig, userExperimentRecord);
}

if (clientEngine == null) {
clientEngine = ClientEngine.JAVA_SDK;
}

if (clientVersion == null) {
clientVersion = BuildVersionInfo.VERSION;
}

if (eventBuilder == null) {
if (projectConfig.getVersion().equals(ProjectConfig.V1)) {
eventBuilder = new EventBuilderV1();
} else {
eventBuilder = new EventBuilderV2();
eventBuilder = new EventBuilderV2(clientEngine, clientVersion);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package com.optimizely.ab.event.internal;

import com.optimizely.ab.annotations.VisibleForTesting;
import com.optimizely.ab.bucketing.Bucketer;
import com.optimizely.ab.config.Attribute;
import com.optimizely.ab.config.Experiment;
Expand All @@ -25,6 +26,7 @@
import com.optimizely.ab.event.internal.payload.Conversion;
import com.optimizely.ab.event.internal.payload.Decision;
import com.optimizely.ab.event.internal.payload.EventMetric;
import com.optimizely.ab.event.internal.payload.Event.ClientEngine;
import com.optimizely.ab.event.internal.payload.Feature;
import com.optimizely.ab.event.internal.payload.Impression;
import com.optimizely.ab.event.internal.payload.LayerState;
Expand Down Expand Up @@ -52,9 +54,21 @@ public class EventBuilderV2 extends EventBuilder {
static final String IMPRESSION_ENDPOINT = "https://p13nlog.dz.optimizely.com/log/decision";
static final String CONVERSION_ENDPOINT = "https://p13nlog.dz.optimizely.com/log/event";

@VisibleForTesting
public final ClientEngine clientEngine;

@VisibleForTesting
public final String clientVersion;

private Serializer serializer;

public EventBuilderV2() {
this(ClientEngine.JAVA_SDK, BuildVersionInfo.VERSION);
}

public EventBuilderV2(ClientEngine clientEngine, String clientVersion) {
this.clientEngine = clientEngine;
this.clientVersion = clientVersion;
this.serializer = DefaultJsonSerializer.getInstance();
}

Expand All @@ -79,6 +93,8 @@ public LogEvent createImpressionEvent(@Nonnull ProjectConfig projectConfig,
impressionPayload.setLayerId(activatedExperiment.getLayerId());
impressionPayload.setAccountId(projectConfig.getAccountId());
impressionPayload.setUserFeatures(createFeatures(attributes, projectConfig));
impressionPayload.setClientEngine(clientEngine);
impressionPayload.setClientVersion(clientVersion);

String payload = this.serializer.serialize(impressionPayload);
return new LogEvent(RequestMethod.POST, IMPRESSION_ENDPOINT, Collections.<String, String>emptyMap(), payload);
Expand Down Expand Up @@ -117,6 +133,8 @@ public LogEvent createConversionEvent(@Nonnull ProjectConfig projectConfig,

conversionPayload.setEventFeatures(Collections.<Feature>emptyList());
conversionPayload.setIsGlobalHoldback(false);
conversionPayload.setClientEngine(clientEngine);
conversionPayload.setClientVersion(clientVersion);

String payload = this.serializer.serialize(conversionPayload);
return new LogEvent(RequestMethod.POST, CONVERSION_ENDPOINT, Collections.<String, String>emptyMap(), payload);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,43 @@
*/
package com.optimizely.ab.event.internal.payload;

import com.optimizely.ab.annotations.VisibleForTesting;
import com.fasterxml.jackson.annotation.JsonValue;

import com.optimizely.ab.event.internal.BuildVersionInfo;

public class Event {

String clientEngine = "java-sdk";
public enum ClientEngine {
JAVA_SDK ("java-sdk"),
ANDROID_SDK ("android-sdk");

private final String clientEngineValue;

ClientEngine(String clientEngineValue) {
this.clientEngineValue = clientEngineValue;
}

@JsonValue
public String getClientEngineValue() {
return clientEngineValue;
}
}

String clientEngine = ClientEngine.JAVA_SDK.getClientEngineValue();
String clientVersion = BuildVersionInfo.VERSION;

public String getClientEngine() {
return clientEngine;
}

public void setClientEngine(ClientEngine clientEngine) {
this.clientEngine = clientEngine.getClientEngineValue();
}

public String getClientVersion() {
return clientVersion;
}

@VisibleForTesting
public void setClientVersion(String clientVersion) {
this.clientVersion = clientVersion;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@
*/
package com.optimizely.ab;

import ch.qos.logback.classic.Level;

import com.optimizely.ab.bucketing.UserExperimentRecord;
import com.optimizely.ab.config.ProjectConfigTestUtils;
import com.optimizely.ab.config.parser.ConfigParseException;
import com.optimizely.ab.error.ErrorHandler;
import com.optimizely.ab.error.NoOpErrorHandler;
import com.optimizely.ab.event.EventHandler;
import com.optimizely.ab.internal.LogbackVerifier;
import com.optimizely.ab.event.internal.BuildVersionInfo;
import com.optimizely.ab.event.internal.EventBuilderV2;
import com.optimizely.ab.event.internal.payload.Event.ClientEngine;

import org.junit.Rule;
import org.junit.Test;
Expand All @@ -41,7 +41,6 @@
import static com.optimizely.ab.config.ProjectConfigTestUtils.validProjectConfigV2;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;

Expand All @@ -57,9 +56,6 @@ public class OptimizelyBuilderTest {
@Rule
public MockitoRule rule = MockitoJUnit.rule();

@Rule
public LogbackVerifier logbackVerifier = new LogbackVerifier();

@Mock private EventHandler mockEventHandler;

@Mock private ErrorHandler mockErrorHandler;
Expand Down Expand Up @@ -115,6 +111,40 @@ public void withUserExperimentRecord() throws Exception {
assertThat(optimizelyClient.bucketer.getUserExperimentRecord(), is(userExperimentRecord));
}

@Test
public void withDefaultClientEngine() throws Exception {
Optimizely optimizelyClient = Optimizely.builder(validConfigJsonV2(), mockEventHandler)
.build();

assertThat(((EventBuilderV2)optimizelyClient.eventBuilder).clientEngine, is(ClientEngine.JAVA_SDK));
}

@Test
public void withCustomClientEngine() throws Exception {
Optimizely optimizelyClient = Optimizely.builder(validConfigJsonV2(), mockEventHandler)
.withClientEngine(ClientEngine.ANDROID_SDK)
.build();

assertThat(((EventBuilderV2)optimizelyClient.eventBuilder).clientEngine, is(ClientEngine.ANDROID_SDK));
}

@Test
public void withDefaultClientVersion() throws Exception {
Optimizely optimizelyClient = Optimizely.builder(validConfigJsonV2(), mockEventHandler)
.build();

assertThat(((EventBuilderV2)optimizelyClient.eventBuilder).clientVersion, is(BuildVersionInfo.VERSION));
}

@Test
public void withCustomClientVersion() throws Exception {
Optimizely optimizelyClient = Optimizely.builder(validConfigJsonV2(), mockEventHandler)
.withClientVersion("0.0.0")
.build();

assertThat(((EventBuilderV2)optimizelyClient.eventBuilder).clientVersion, is("0.0.0"));
}

@Test
public void builderThrowsConfigParseExceptionForInvalidDatafile() throws Exception {
thrown.expect(ConfigParseException.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@
import com.optimizely.ab.error.RaiseExceptionErrorHandler;
import com.optimizely.ab.event.EventHandler;
import com.optimizely.ab.event.LogEvent;
import com.optimizely.ab.event.internal.BuildVersionInfo;
import com.optimizely.ab.event.internal.EventBuilder;
import com.optimizely.ab.event.internal.EventBuilderV2;
import com.optimizely.ab.event.internal.payload.Event.ClientEngine;
import com.optimizely.ab.internal.LogbackVerifier;
import com.optimizely.ab.internal.ProjectValidationUtils;

Expand Down Expand Up @@ -788,9 +790,9 @@ public void trackEventWithAttributes() throws Exception {
testParams.put("test", "params");
LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, "");
when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"),
eq(eventType.getId()), eq(eventType.getKey()),
anyMapOf(String.class, String.class)))
.thenReturn(logEventToDispatch);
eq(eventType.getId()), eq(eventType.getKey()),
anyMapOf(String.class, String.class)))
.thenReturn(logEventToDispatch);

logbackVerifier.expectMessage(Level.INFO, "Tracking event \"clicked_cart\" for user \"userId\".");
logbackVerifier.expectMessage(Level.DEBUG, "Dispatching conversion event to URL test_url with params " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.optimizely.ab.config.Variation;
import com.optimizely.ab.event.internal.payload.Conversion;
import com.optimizely.ab.event.internal.payload.Decision;
import com.optimizely.ab.event.internal.payload.Event.ClientEngine;
import com.optimizely.ab.event.internal.payload.EventMetric;
import com.optimizely.ab.event.internal.payload.Feature;
import com.optimizely.ab.event.internal.payload.Impression;
Expand Down Expand Up @@ -94,7 +95,7 @@ public void createImpressionEvent() throws Exception {
assertThat(impression.getLayerId(), is(activatedExperiment.getLayerId()));
assertThat(impression.getAccountId(), is(projectConfig.getAccountId()));
assertThat(impression.getUserFeatures(), is(expectedUserFeatures));
assertThat(impression.getClientEngine(), is("java-sdk"));
assertThat(impression.getClientEngine(), is(ClientEngine.JAVA_SDK.getClientEngineValue()));
assertThat(impression.getClientVersion(), is(BuildVersionInfo.VERSION));
}

Expand Down Expand Up @@ -122,6 +123,28 @@ public void createImpressionEventIgnoresUnknownAttributes() throws Exception {
}
}

/**
* Verify that supplying {@link EventBuilderV2} with a custom client engine and client version results in impression
* events being sent with the overriden values.
*/
@Test
public void createImpressionEventCustomClientEngineClientVersion() throws Exception {
EventBuilderV2 builder = new EventBuilderV2(ClientEngine.ANDROID_SDK, "0.0.0");
ProjectConfig projectConfig = ProjectConfigTestUtils.validProjectConfigV2();
Experiment activatedExperiment = projectConfig.getExperiments().get(0);
Variation bucketedVariation = activatedExperiment.getVariations().get(0);
Attribute attribute = projectConfig.getAttributes().get(0);
String userId = "userId";
Map<String, String> attributeMap = Collections.singletonMap(attribute.getKey(), "value");

LogEvent impressionEvent = builder.createImpressionEvent(projectConfig, activatedExperiment, bucketedVariation,
userId, attributeMap);
Impression impression = gson.fromJson(impressionEvent.getBody(), Impression.class);

assertThat(impression.getClientEngine(), is(ClientEngine.ANDROID_SDK.getClientEngineValue()));
assertThat(impression.getClientVersion(), is("0.0.0"));
}

/**
* Verify {@link Conversion} event creation
*/
Expand Down Expand Up @@ -184,7 +207,7 @@ public void createConversionEvent() throws Exception {
assertThat(conversion.getEventMetrics(), is(Collections.<EventMetric>emptyList()));
assertThat(conversion.getEventFeatures(), is(Collections.<Feature>emptyList()));
assertFalse(conversion.getIsGlobalHoldback());
assertThat(conversion.getClientEngine(), is("java-sdk"));
assertThat(conversion.getClientEngine(), is(ClientEngine.JAVA_SDK.getClientEngineValue()));
assertThat(conversion.getClientVersion(), is(BuildVersionInfo.VERSION));
}

Expand Down Expand Up @@ -252,8 +275,6 @@ public void createConversionParamsUserNotInAudience() throws Exception {
*/
@Test
public void createConversionEventForcedVariationBucketingPrecedesAudienceEval() {
EventBuilderV2 builder = new EventBuilderV2();

ProjectConfig projectConfig = ProjectConfigTestUtils.validProjectConfigV2();
EventType eventType = projectConfig.getEventTypes().get(0);
String userId = "testUser1";
Expand Down Expand Up @@ -293,8 +314,6 @@ public void createConversionEventForcedVariationBucketingPrecedesAudienceEval()
*/
@Test
public void createConversionEventExperimentStatusPrecedesForcedVariation() {
EventBuilderV2 builder = new EventBuilderV2();

ProjectConfig projectConfig = ProjectConfigTestUtils.validProjectConfigV2();
EventType eventType = projectConfig.getEventTypes().get(3);
String userId = "userId";
Expand All @@ -315,4 +334,32 @@ public void createConversionEventExperimentStatusPrecedesForcedVariation() {

assertNull(conversionEvent);
}

/**
* Verify that supplying {@link EventBuilderV2} with a custom client engine and client version results in conversion
* events being sent with the overriden values.
*/
@Test
public void createConversionEventCustomClientEngineClientVersion() throws Exception {
EventBuilderV2 builder = new EventBuilderV2(ClientEngine.ANDROID_SDK, "0.0.0");
ProjectConfig projectConfig = ProjectConfigTestUtils.validProjectConfigV2();
Attribute attribute = projectConfig.getAttributes().get(0);
EventType eventType = projectConfig.getEventTypes().get(0);
String userId = "userId";

Bucketer mockBucketAlgorithm = mock(Bucketer.class);
for (Experiment experiment : projectConfig.getExperiments()) {
when(mockBucketAlgorithm.bucket(experiment, userId))
.thenReturn(experiment.getVariations().get(0));
}

Map<String, String> attributeMap = Collections.singletonMap(attribute.getKey(), "value");
LogEvent conversionEvent = builder.createConversionEvent(projectConfig, mockBucketAlgorithm, userId,
eventType.getId(), eventType.getKey(), attributeMap);

Conversion conversion = gson.fromJson(conversionEvent.getBody(), Conversion.class);

assertThat(conversion.getClientEngine(), is(ClientEngine.ANDROID_SDK.getClientEngineValue()));
assertThat(conversion.getClientVersion(), is("0.0.0"));
}
}