diff --git a/core-api/src/main/java/com/optimizely/ab/Optimizely.java b/core-api/src/main/java/com/optimizely/ab/Optimizely.java index 6e36624bc..3b2f66113 100644 --- a/core-api/src/main/java/com/optimizely/ab/Optimizely.java +++ b/core-api/src/main/java/com/optimizely/ab/Optimizely.java @@ -37,8 +37,7 @@ 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.event.internal.payload.EventBatch.ClientEngine; import com.optimizely.ab.internal.EventTagUtils; import com.optimizely.ab.notification.NotificationBroadcaster; import com.optimizely.ab.notification.NotificationCenter; @@ -948,7 +947,7 @@ public Optimizely build() throws ConfigParseException { if (eventBuilder == null) { - eventBuilder = new EventBuilderV2(clientEngine, clientVersion); + eventBuilder = new EventBuilder(clientEngine, clientVersion); } if (errorHandler == null) { diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/EventBuilder.java b/core-api/src/main/java/com/optimizely/ab/event/internal/EventBuilder.java index 05da9e084..20206d343 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/EventBuilder.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/EventBuilder.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016-2017, Optimizely and contributors + * Copyright 2016-2018, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,27 +16,122 @@ */ package com.optimizely.ab.event.internal; +import com.optimizely.ab.annotations.VisibleForTesting; +import com.optimizely.ab.bucketing.DecisionService; +import com.optimizely.ab.config.EventType; import com.optimizely.ab.config.Experiment; import com.optimizely.ab.config.ProjectConfig; import com.optimizely.ab.config.Variation; import com.optimizely.ab.event.LogEvent; - +import com.optimizely.ab.event.internal.payload.Attribute; +import com.optimizely.ab.event.internal.payload.Decision; +import com.optimizely.ab.event.internal.payload.EventBatch; +import com.optimizely.ab.event.internal.payload.Event; +import com.optimizely.ab.event.internal.payload.Snapshot; +import com.optimizely.ab.event.internal.payload.Visitor; +import com.optimizely.ab.event.internal.serializer.DefaultJsonSerializer; +import com.optimizely.ab.event.internal.serializer.Serializer; +import com.optimizely.ab.internal.EventTagUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Map; +import java.util.UUID; + +public class EventBuilder { + private static final Logger logger = LoggerFactory.getLogger(EventBuilder.class); + static final String ATTRIBUTE_KEY_FOR_BUCKETING_ATTRIBUTE = "optimizely_bucketing_id"; + static final String EVENT_ENDPOINT = "https://logx.optimizely.com/v1/events"; + static final String ACTIVATE_EVENT_KEY = "campaign_activated"; + + private Serializer serializer; + @VisibleForTesting + public final String clientVersion; + @VisibleForTesting + public final EventBatch.ClientEngine clientEngine; + + public EventBuilder() { + this(EventBatch.ClientEngine.JAVA_SDK, BuildVersionInfo.VERSION); + } + + public EventBuilder(EventBatch.ClientEngine clientEngine, String clientVersion) { + this.clientEngine = clientEngine; + this.clientVersion = clientVersion; + this.serializer = DefaultJsonSerializer.getInstance(); + } -public abstract class EventBuilder { - public abstract LogEvent createImpressionEvent(@Nonnull ProjectConfig projectConfig, + public LogEvent createImpressionEvent(@Nonnull ProjectConfig projectConfig, @Nonnull Experiment activatedExperiment, @Nonnull Variation variation, @Nonnull String userId, - @Nonnull Map attributes); + @Nonnull Map attributes) { - public abstract LogEvent createConversionEvent(@Nonnull ProjectConfig projectConfig, + Decision decision = new Decision(activatedExperiment.getLayerId(), activatedExperiment.getId(), + variation.getId(), false); + Event impressionEvent = new Event(System.currentTimeMillis(),UUID.randomUUID().toString(), activatedExperiment.getLayerId(), + ACTIVATE_EVENT_KEY, null, null, null, ACTIVATE_EVENT_KEY, null); + Snapshot snapshot = new Snapshot(Arrays.asList(decision), Arrays.asList(impressionEvent)); + + Visitor visitor = new Visitor(userId, null, buildAttributeList(projectConfig, attributes), Arrays.asList(snapshot)); + List visitors = Arrays.asList(visitor); + EventBatch eventBatch = new EventBatch(clientEngine.getClientEngineValue(), clientVersion, projectConfig.getAccountId(), visitors, projectConfig.getAnonymizeIP(), projectConfig.getProjectId(), projectConfig.getRevision()); + String payload = this.serializer.serialize(eventBatch); + return new LogEvent(LogEvent.RequestMethod.POST, EVENT_ENDPOINT, Collections.emptyMap(), payload); + } + + public LogEvent createConversionEvent(@Nonnull ProjectConfig projectConfig, @Nonnull Map experimentVariationMap, @Nonnull String userId, @Nonnull String eventId, @Nonnull String eventName, @Nonnull Map attributes, - @Nonnull Map eventTags); + @Nonnull Map eventTags) { + + if (experimentVariationMap.isEmpty()) { + return null; + } + + ArrayList decisions = new ArrayList(); + for (Map.Entry entry : experimentVariationMap.entrySet()) { + Decision decision = new Decision(entry.getKey().getLayerId(), entry.getKey().getId(), entry.getValue().getId(), false); + decisions.add(decision); + } + + EventType eventType = projectConfig.getEventNameMapping().get(eventName); + + Event conversionEvent = new Event(System.currentTimeMillis(),UUID.randomUUID().toString(), eventType.getId(), + eventType.getKey(), null, EventTagUtils.getRevenueValue(eventTags), eventTags, eventType.getKey(), EventTagUtils.getNumericValue(eventTags)); + Snapshot snapshot = new Snapshot(decisions, Arrays.asList(conversionEvent)); + + Visitor visitor = new Visitor(userId, null, buildAttributeList(projectConfig, attributes), Arrays.asList(snapshot)); + List visitors = Arrays.asList(visitor); + EventBatch eventBatch = new EventBatch(clientEngine.getClientEngineValue(), clientVersion, projectConfig.getAccountId(), visitors, projectConfig.getAnonymizeIP(), projectConfig.getProjectId(), projectConfig.getRevision()); + String payload = this.serializer.serialize(eventBatch); + return new LogEvent(LogEvent.RequestMethod.POST, EVENT_ENDPOINT, Collections.emptyMap(), payload); + } + + private List buildAttributeList(ProjectConfig projectConfig, Map attributes) { + List attributesList = new ArrayList(); + + Map attributeMap = projectConfig.getAttributeKeyMapping(); + for (Map.Entry entry : attributes.entrySet()) { + com.optimizely.ab.config.Attribute projectAttribute = attributeMap.get(entry.getKey()); + Attribute attribute = new Attribute((projectAttribute != null ? projectAttribute.getId() : null), + entry.getKey(), Attribute.CUSTOM_ATTRIBUTE_TYPE, entry.getValue()); + + if (entry.getKey() == DecisionService.BUCKETING_ATTRIBUTE) { + attribute = new Attribute(com.optimizely.ab.bucketing.DecisionService.BUCKETING_ATTRIBUTE, + ATTRIBUTE_KEY_FOR_BUCKETING_ATTRIBUTE, Attribute.CUSTOM_ATTRIBUTE_TYPE, entry.getValue()); + } + + attributesList.add(attribute); + } + + return attributesList; + } } diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/EventBuilderV2.java b/core-api/src/main/java/com/optimizely/ab/event/internal/EventBuilderV2.java deleted file mode 100644 index 2a538c33a..000000000 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/EventBuilderV2.java +++ /dev/null @@ -1,233 +0,0 @@ -/** - * - * Copyright 2016-2017, Optimizely and contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.optimizely.ab.event.internal; - -import com.optimizely.ab.annotations.VisibleForTesting; -import com.optimizely.ab.bucketing.DecisionService; -import com.optimizely.ab.config.Attribute; -import com.optimizely.ab.config.Experiment; -import com.optimizely.ab.config.ProjectConfig; -import com.optimizely.ab.config.Variation; -import com.optimizely.ab.event.LogEvent; -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; -import com.optimizely.ab.event.internal.payload.LayerState; -import com.optimizely.ab.event.internal.serializer.DefaultJsonSerializer; -import com.optimizely.ab.event.internal.serializer.Serializer; -import com.optimizely.ab.internal.EventTagUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nonnull; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import static com.optimizely.ab.event.LogEvent.RequestMethod; - -public class EventBuilderV2 extends EventBuilder { - - private static final Logger logger = LoggerFactory.getLogger(EventBuilderV2.class); - - static final String IMPRESSION_ENDPOINT = "https://logx.optimizely.com/log/decision"; - static final String CONVERSION_ENDPOINT = "https://logx.optimizely.com/log/event"; - static final String ATTRIBUTE_KEY_FOR_BUCKETING_ATTRIBUTE = "optimizely_bucketing_id"; - - @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(); - } - - public LogEvent createImpressionEvent(@Nonnull ProjectConfig projectConfig, - @Nonnull Experiment activatedExperiment, - @Nonnull Variation variation, - @Nonnull String userId, - @Nonnull Map attributes) { - - Impression impressionPayload = new Impression(); - impressionPayload.setVisitorId(userId); - impressionPayload.setTimestamp(System.currentTimeMillis()); - impressionPayload.setIsGlobalHoldback(false); - impressionPayload.setProjectId(projectConfig.getProjectId()); - - Decision decision = new Decision(); - decision.setVariationId(variation.getId()); - decision.setIsLayerHoldback(false); - decision.setExperimentId(activatedExperiment.getId()); - impressionPayload.setDecision(decision); - - impressionPayload.setLayerId(activatedExperiment.getLayerId()); - impressionPayload.setAccountId(projectConfig.getAccountId()); - impressionPayload.setUserFeatures(createUserFeatures(attributes, projectConfig)); - impressionPayload.setClientEngine(clientEngine); - impressionPayload.setClientVersion(clientVersion); - impressionPayload.setAnonymizeIP(projectConfig.getAnonymizeIP()); - impressionPayload.setRevision(projectConfig.getRevision()); - - String payload = this.serializer.serialize(impressionPayload); - return new LogEvent(RequestMethod.POST, IMPRESSION_ENDPOINT, Collections.emptyMap(), payload); - } - - public LogEvent createConversionEvent(@Nonnull ProjectConfig projectConfig, - @Nonnull Map experimentVariationMap, - @Nonnull String userId, - @Nonnull String eventId, - @Nonnull String eventName, - @Nonnull Map attributes, - @Nonnull Map eventTags) { - - if (experimentVariationMap.isEmpty()) { - return null; - } - - List layerStates = createLayerStates(projectConfig, experimentVariationMap); - - List eventMetrics = new ArrayList(); - - Long revenueValue = EventTagUtils.getRevenueValue(eventTags); - if (revenueValue != null) { - eventMetrics.add(new EventMetric(EventMetric.REVENUE_METRIC_TYPE, revenueValue)); - } - - Double numericMetricValue = EventTagUtils.getNumericValue(eventTags); - if (numericMetricValue != null) { - eventMetrics.add(new EventMetric(EventMetric.NUMERIC_METRIC_TYPE, numericMetricValue)); - } - - Conversion conversionPayload = new Conversion(); - conversionPayload.setAccountId(projectConfig.getAccountId()); - conversionPayload.setAnonymizeIP(projectConfig.getAnonymizeIP()); - conversionPayload.setClientEngine(clientEngine); - conversionPayload.setClientVersion(clientVersion); - conversionPayload.setEventEntityId(eventId); - conversionPayload.setEventFeatures(createEventFeatures(eventTags)); - conversionPayload.setEventName(eventName); - conversionPayload.setEventMetrics(eventMetrics); - conversionPayload.setIsGlobalHoldback(false); - conversionPayload.setLayerStates(layerStates); - conversionPayload.setProjectId(projectConfig.getProjectId()); - conversionPayload.setRevision(projectConfig.getRevision()); - conversionPayload.setTimestamp(System.currentTimeMillis()); - conversionPayload.setUserFeatures(createUserFeatures(attributes, projectConfig)); - conversionPayload.setVisitorId(userId); - - String payload = this.serializer.serialize(conversionPayload); - return new LogEvent(RequestMethod.POST, CONVERSION_ENDPOINT, Collections.emptyMap(), payload); - } - - /** - * Helper method to generate {@link Feature} objects from the given {@code {attributeKey -> value}} mapping. - * - * @param attributes the {@code {attributeKey -> value}} mapping - * @param projectConfig the current project config - */ - private List createUserFeatures(Map attributes, ProjectConfig projectConfig) { - Map attributeKeyMapping = projectConfig.getAttributeKeyMapping(); - List features = new ArrayList(); - - for (Map.Entry attributeEntry : attributes.entrySet()) { - String attributeKey = attributeEntry.getKey(); - Attribute attribute = attributeKeyMapping.get(attributeKey); - - if (attributeEntry.getKey() == DecisionService.BUCKETING_ATTRIBUTE) { - features.add(new Feature(com.optimizely.ab.bucketing.DecisionService.BUCKETING_ATTRIBUTE, - ATTRIBUTE_KEY_FOR_BUCKETING_ATTRIBUTE, - Feature.CUSTOM_ATTRIBUTE_FEATURE_TYPE, - attributeEntry.getValue(), true)); - continue; - } - - if (attribute == null) { - logger.warn("Attempting to use unknown attribute key: {}. Attribute will be ignored", attributeKey); - continue; - } - - features.add(new Feature(attribute.getId(), attributeKey, Feature.CUSTOM_ATTRIBUTE_FEATURE_TYPE, - attributeEntry.getValue(), true)); - - } - - return features; - } - - /** - * Helper method to generate {@link Feature} objects from the given {@code {eventTagKey-> value}} mapping. - * - * @param eventTags the {@code {eventTagKey -> value}} mapping - */ - private List createEventFeatures(Map eventTags) { - List features = new ArrayList(); - - for (Map.Entry eventTagEntry : eventTags.entrySet()) { - String eventTagKey = eventTagEntry.getKey(); - features.add(new Feature("", eventTagKey, Feature.EVENT_FEATURE_TYPE, eventTagEntry.getValue(), false)); - } - return features; - } - - /** - * Helper method to create {@link LayerState} objects for all experiments mapped to an event. - *

- * Note, although the user may not have activated all experiments, we include all possible experiment - * mappings so that activated experiment state doesn't have to be maintained and passed around server-side. - *

- * For example, in a project with multiple experiments, if user 'user1' activates experiment 'exp1' and then - * converts, we need to ensure we attribute that conversion to 'exp1'. We can either do that by recording activation - * and passing that state around so it's available on conversion, or we can send all possible activations at - * conversion-time and attribute conversions only to the experiments for which we also received an - * impression. - *

- * This is referred to as "visitor first counting". It's important to filter the bucket map as much as possible, - * as any experiments sent through that don't have a corresponding impression are simply occupying space for - * no good reason. - * - * @param projectConfig the current project config - * @param experimentVariationMap the mapping of experiments associated with this event - * and the variations the user was bucketed into for that experiment - * - */ - private List createLayerStates(ProjectConfig projectConfig, Map experimentVariationMap) { - List layerStates = new ArrayList(); - - for (Map.Entry entry : experimentVariationMap.entrySet()) { - Experiment experiment = entry.getKey(); - Variation variation = entry.getValue(); - Decision decision = new Decision(variation.getId(), false, experiment.getId()); - layerStates.add(new LayerState(experiment.getLayerId(), projectConfig.getRevision(), decision, true)); - } - - return layerStates; - } -} diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Attribute.java b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Attribute.java new file mode 100644 index 000000000..71e93adba --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Attribute.java @@ -0,0 +1,95 @@ +/** + * + * Copyright 2018, Optimizely and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.optimizely.ab.event.internal.payload; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class Attribute { + public static final String CUSTOM_ATTRIBUTE_TYPE = "custom"; + public static final String CUSTOM_EVENT_TYPE = "custom"; + + @JsonProperty("entity_id") + String entityId; + String key; + String type; + String value; + + public Attribute() { + + } + + public Attribute(String entityId, String key, String type, String value) { + this.entityId = entityId; + this.key = key; + this.type = type; + this.value = value; + } + + public String getEntityId() { + return entityId; + } + + public void setEntityId(String entityId) { + this.entityId = entityId; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Attribute attribute = (Attribute) o; + + if (!entityId.equals(attribute.entityId)) return false; + if (!key.equals(attribute.key)) return false; + if (!type.equals(attribute.type)) return false; + return value.equals(attribute.value); + } + + @Override + public int hashCode() { + int result = entityId.hashCode(); + result = 31 * result + key.hashCode(); + result = 31 * result + type.hashCode(); + result = 31 * result + value.hashCode(); + return result; + } +} diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Conversion.java b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Conversion.java deleted file mode 100644 index 724dcc2b0..000000000 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Conversion.java +++ /dev/null @@ -1,239 +0,0 @@ -/** - * - * Copyright 2016-2017, Optimizely and contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.optimizely.ab.event.internal.payload; - -import java.util.List; - -public class Conversion extends Event { - - private String accountId; - private boolean anonymizeIP; - private String eventEntityId; - private List eventFeatures; - private List eventMetrics; - private String eventName; - private boolean isGlobalHoldback; - private List layerStates; - private String projectId; - private String revision; - private String sessionId; - private long timestamp; - private List userFeatures; - private String visitorId; - - public Conversion() { } - - public Conversion(String visitorId, long timestamp, String projectId, String accountId, List userFeatures, - List layerStates, String eventEntityId, String eventName, - List eventMetrics, List eventFeatures, boolean isGlobalHoldback, - String revision, boolean anonymizeIP) { - this(visitorId, timestamp, projectId, accountId, userFeatures, layerStates, eventEntityId, eventName, - eventMetrics, eventFeatures, isGlobalHoldback, anonymizeIP, revision, null); - } - - public Conversion(String visitorId, long timestamp, String projectId, String accountId, List userFeatures, - List layerStates, String eventEntityId, String eventName, - List eventMetrics, List eventFeatures, boolean isGlobalHoldback, - boolean anonymizeIP, String revision, String sessionId) { - this.accountId = accountId; - this.anonymizeIP = anonymizeIP; - this.eventEntityId = eventEntityId; - this.eventFeatures = eventFeatures; - this.eventMetrics = eventMetrics; - this.eventName = eventName; - this.isGlobalHoldback = isGlobalHoldback; - this.layerStates = layerStates; - this.projectId = projectId; - this.revision = revision; - this.sessionId = sessionId; - this.timestamp = timestamp; - this.userFeatures = userFeatures; - this.visitorId = visitorId; - } - - public String getVisitorId() { - return visitorId; - } - - public void setVisitorId(String visitorId) { - this.visitorId = visitorId; - } - - public long getTimestamp() { - return timestamp; - } - - public void setTimestamp(long timestamp) { - this.timestamp = timestamp; - } - - public String getProjectId() { - return projectId; - } - - public void setProjectId(String projectId) { - this.projectId = projectId; - } - - public String getAccountId() { - return accountId; - } - - public void setAccountId(String accountId) { - this.accountId = accountId; - } - - public List getUserFeatures() { - return userFeatures; - } - - public void setUserFeatures(List userFeatures) { - this.userFeatures = userFeatures; - } - - public List getLayerStates() { - return layerStates; - } - - public void setLayerStates(List layerStates) { - this.layerStates = layerStates; - } - - public String getEventEntityId() { - return eventEntityId; - } - - public void setEventEntityId(String eventEntityId) { - this.eventEntityId = eventEntityId; - } - - public String getEventName() { - return eventName; - } - - public void setEventName(String eventName) { - this.eventName = eventName; - } - - public List getEventMetrics() { - return eventMetrics; - } - - public void setEventMetrics(List eventMetrics) { - this.eventMetrics = eventMetrics; - } - - public List getEventFeatures() { - return eventFeatures; - } - - public void setEventFeatures(List eventFeatures) { - this.eventFeatures = eventFeatures; - } - - public boolean getIsGlobalHoldback() { - return isGlobalHoldback; - } - - public void setIsGlobalHoldback(boolean globalHoldback) { - this.isGlobalHoldback = globalHoldback; - } - - public boolean getAnonymizeIP() { return anonymizeIP; } - - public void setAnonymizeIP(boolean anonymizeIP) { this.anonymizeIP = anonymizeIP; } - - public String getSessionId() { - return sessionId; - } - - public void setSessionId(String sessionId) { - this.sessionId = sessionId; - } - - public String getRevision() { - return revision; - } - - public void setRevision(String revision) { - this.revision = revision; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; - - Conversion that = (Conversion) o; - - if (timestamp != that.timestamp) return false; - if (isGlobalHoldback != that.isGlobalHoldback) return false; - if (anonymizeIP != that.anonymizeIP) return false; - if (!visitorId.equals(that.visitorId)) return false; - if (!projectId.equals(that.projectId)) return false; - if (!accountId.equals(that.accountId)) return false; - if (!userFeatures.equals(that.userFeatures)) return false; - if (!layerStates.equals(that.layerStates)) return false; - if (!eventEntityId.equals(that.eventEntityId)) return false; - if (!eventName.equals(that.eventName)) return false; - if (!eventMetrics.equals(that.eventMetrics)) return false; - if (!eventFeatures.equals(that.eventFeatures)) return false; - if (sessionId != null ? !sessionId.equals(that.sessionId) : that.sessionId != null) return false; - return revision.equals(that.revision); - } - - @Override - public int hashCode() { - int result = super.hashCode(); - result = 31 * result + visitorId.hashCode(); - result = 31 * result + (int) (timestamp ^ (timestamp >>> 32)); - result = 31 * result + projectId.hashCode(); - result = 31 * result + accountId.hashCode(); - result = 31 * result + userFeatures.hashCode(); - result = 31 * result + layerStates.hashCode(); - result = 31 * result + eventEntityId.hashCode(); - result = 31 * result + eventName.hashCode(); - result = 31 * result + eventMetrics.hashCode(); - result = 31 * result + eventFeatures.hashCode(); - result = 31 * result + (isGlobalHoldback ? 1 : 0); - result = 31 * result + (anonymizeIP ? 1 : 0); - result = 31 * result + (sessionId != null ? sessionId.hashCode() : 0); - result = 31 * result + revision.hashCode(); - return result; - } - - @Override - public String toString() { - return "Conversion{" + - "accountId='" + accountId + '\'' + - ", anonymizeIP=" + anonymizeIP + - ", eventEntityId='" + eventEntityId + '\'' + - ", eventFeatures=" + eventFeatures + - ", eventMetrics=" + eventMetrics + - ", eventName='" + eventName + '\'' + - ", isGlobalHoldback=" + isGlobalHoldback + - ", layerStates=" + layerStates + - ", projectId='" + projectId + '\'' + - ", revision='" + revision + '\'' + - ", sessionId='" + sessionId + '\'' + - ", timestamp=" + timestamp + - ", userFeatures=" + userFeatures + - ", visitorId='" + visitorId + '\'' + - '}'; - } -} diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Decision.java b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Decision.java index d641552a1..8ffa8d23c 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Decision.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Decision.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016-2017, Optimizely and contributors + * Copyright 2018, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,34 +16,35 @@ */ package com.optimizely.ab.event.internal.payload; -public class Decision { - - private String variationId; - private boolean isLayerHoldback; - private String experimentId; +import com.fasterxml.jackson.annotation.JsonProperty; - public Decision() {} +public class Decision { + @JsonProperty("campaign_id") + String campaignId; + @JsonProperty("experiment_id") + String experimentId; + @JsonProperty("variation_id") + String variationId; + @JsonProperty("is_campaign_holdback") + boolean isCampaignHoldback; - public Decision(String variationId, boolean isLayerHoldback, String experimentId) { - this.variationId = variationId; - this.isLayerHoldback = isLayerHoldback; + public Decision(String campaignId, String experimentId, String variationId, boolean isCampaignHoldback) { + this.campaignId = campaignId; this.experimentId = experimentId; + this.variationId = variationId; + this.isCampaignHoldback = isCampaignHoldback; } - public String getVariationId() { - return variationId; - } + public Decision() { - public void setVariationId(String variationId) { - this.variationId = variationId; } - public boolean getIsLayerHoldback() { - return isLayerHoldback; + public String getCampaignId() { + return campaignId; } - public void setIsLayerHoldback(boolean layerHoldback) { - this.isLayerHoldback = layerHoldback; + public void setCampaignId(String campaignId) { + this.campaignId = campaignId; } public String getExperimentId() { @@ -54,32 +55,41 @@ public void setExperimentId(String experimentId) { this.experimentId = experimentId; } + public String getVariationId() { + return variationId; + } + + public void setVariationId(String variationId) { + this.variationId = variationId; + } + + public boolean getIsCampaignHoldback() { + return isCampaignHoldback; + } + + public void setIsCampaignHoldback(boolean campaignHoldback) { + isCampaignHoldback = campaignHoldback; + } + @Override - public boolean equals(Object other) { - if (!(other instanceof Decision)) - return false; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; - Decision otherDecision = (Decision)other; + Decision that = (Decision) o; - return variationId.equals(otherDecision.getVariationId()) && - isLayerHoldback == otherDecision.getIsLayerHoldback() && - experimentId.equals(otherDecision.getExperimentId()); + if (isCampaignHoldback != that.isCampaignHoldback) return false; + if (!campaignId.equals(that.campaignId)) return false; + if (!experimentId.equals(that.experimentId)) return false; + return variationId.equals(that.variationId); } @Override public int hashCode() { - int result = variationId.hashCode(); - result = 31 * result + (isLayerHoldback ? 1 : 0); + int result = campaignId.hashCode(); result = 31 * result + experimentId.hashCode(); + result = 31 * result + variationId.hashCode(); + result = 31 * result + (isCampaignHoldback ? 1 : 0); return result; } - - @Override - public String toString() { - return "Decision{" + - "variationId='" + variationId + '\'' + - ", isLayerHoldback=" + isLayerHoldback + - ", experimentId='" + experimentId + '\'' + - '}'; - } } diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Event.java b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Event.java index 974a72a4c..9ce40541b 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Event.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Event.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016-2017, Optimizely and contributors + * Copyright 2018, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,63 +16,141 @@ */ package com.optimizely.ab.event.internal.payload; -import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonProperty; -import com.optimizely.ab.event.internal.BuildVersionInfo; +import java.util.Map; public class Event { + long timestamp; + String uuid; + @JsonProperty("entity_id") + String entityId; + String key; + Number quantity; + Number revenue; + Map tags; + String type; + Number value; + + public Event() { - public enum ClientEngine { - JAVA_SDK ("java-sdk"), - ANDROID_SDK ("android-sdk"), - ANDROID_TV_SDK ("android-tv-sdk"); + } - private final String clientEngineValue; + public Event(long timestamp, String uuid, String entityId, String key, Number quantity, + Number revenue, Map tags, String type, Number value) { + this.timestamp = timestamp; + this.uuid = uuid; + this.entityId = entityId; + this.key = key; + this.quantity = quantity; + this.revenue = revenue; + this.tags = tags; + this.type = type; + this.value = value; + } - ClientEngine(String clientEngineValue) { - this.clientEngineValue = clientEngineValue; - } + public long getTimestamp() { + return timestamp; + } - @JsonValue - public String getClientEngineValue() { - return clientEngineValue; - } + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; } - String clientEngine = ClientEngine.JAVA_SDK.getClientEngineValue(); - String clientVersion = BuildVersionInfo.VERSION; + public String getUuid() { + return uuid; + } - public String getClientEngine() { - return clientEngine; + public void setUuid(String uuid) { + this.uuid = uuid; } - public void setClientEngine(ClientEngine clientEngine) { - this.clientEngine = clientEngine.getClientEngineValue(); + public String getEntityId() { + return entityId; } - public String getClientVersion() { - return clientVersion; + public void setEntityId(String entityId) { + this.entityId = entityId; } - public void setClientVersion(String clientVersion) { - this.clientVersion = clientVersion; + public String getKey() { + return key; } - @Override - public boolean equals(Object other) { - if (!(other instanceof Event)) - return false; + public void setKey(String key) { + this.key = key; + } + + public Number getQuantity() { + return quantity; + } + + public void setQuantity(Number quantity) { + this.quantity = quantity; + } - Event otherEvent = (Event)other; + public Number getRevenue() { + return revenue; + } - return clientEngine.equals(otherEvent.clientEngine) && - clientVersion.equals(otherEvent.clientVersion); + public void setRevenue(Number revenue) { + this.revenue = revenue; + } + + public Map getTags() { + return tags; + } + + public void setTags(Map tags) { + this.tags = tags; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Number getValue() { + return value; + } + + public void setValue(Number value) { + this.value = value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Event event = (Event) o; + + if (timestamp != event.timestamp) return false; + if (!uuid.equals(event.uuid)) return false; + if (entityId != null ? !entityId.equals(event.entityId) : event.entityId != null) return false; + if (key != null ? !key.equals(event.key) : event.key != null) return false; + if (quantity != null ? !quantity.equals(event.quantity) : event.quantity != null) return false; + if (revenue != null ? !revenue.equals(event.revenue) : event.revenue != null) return false; + if (tags != null ? !tags.equals(event.tags) : event.tags != null) return false; + if (!type.equals(event.type)) return false; + return value != null ? value.equals(event.value) : event.value == null; } @Override public int hashCode() { - int result = clientEngine.hashCode(); - result = 31 * result + clientVersion.hashCode(); + int result = (int) (timestamp ^ (timestamp >>> 32)); + result = 31 * result + uuid.hashCode(); + result = 31 * result + (entityId != null ? entityId.hashCode() : 0); + result = 31 * result + (key != null ? key.hashCode() : 0); + result = 31 * result + (quantity != null ? quantity.hashCode() : 0); + result = 31 * result + (revenue != null ? revenue.hashCode() : 0); + result = 31 * result + (tags != null ? tags.hashCode() : 0); + result = 31 * result + type.hashCode(); + result = 31 * result + (value != null ? value.hashCode() : 0); return result; } + } diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/EventBatch.java b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/EventBatch.java new file mode 100644 index 000000000..9cee5ead4 --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/EventBatch.java @@ -0,0 +1,162 @@ +/** + * + * Copyright 2018, Optimizely and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.optimizely.ab.event.internal.payload; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; +import com.optimizely.ab.event.internal.BuildVersionInfo; + +import java.util.List; + +public class EventBatch { + public enum ClientEngine { + JAVA_SDK ("java-sdk"), + ANDROID_SDK ("android-sdk"), + ANDROID_TV_SDK ("android-tv-sdk"); + + private final String clientEngineValue; + + ClientEngine(String clientEngineValue) { + this.clientEngineValue = clientEngineValue; + } + + @JsonValue + public String getClientEngineValue() { + return clientEngineValue; + } + } + + @JsonProperty("account_id") + String accountId; + List visitors; + @JsonProperty("anonymize_ip") + Boolean anonymizeIp; + @JsonProperty("client_name") + String clientName = ClientEngine.JAVA_SDK.getClientEngineValue(); + @JsonProperty("client_version") + String clientVersion = BuildVersionInfo.VERSION; + @JsonProperty("project_id") + String projectId; + String revision; + + public EventBatch() { + + } + + public EventBatch(String accountId, List visitors, Boolean anonymizeIp, String projectId, String revision) { + this.accountId = accountId; + this.visitors = visitors; + this.anonymizeIp = anonymizeIp; + this.projectId = projectId; + this.revision = revision; + } + + public EventBatch(String clientName, String clientVersion, String accountId, List visitors, Boolean anonymizeIp, String projectId, String revision) { + this.accountId = accountId; + this.visitors = visitors; + this.anonymizeIp = anonymizeIp; + this.clientName = clientName; + this.clientVersion = clientVersion; + this.projectId = projectId; + this.revision = revision; + } + + public String getAccountId() { + return accountId; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } + + public List getVisitors() { + return visitors; + } + + public void setVisitors(List visitors) { + this.visitors = visitors; + } + + public Boolean getAnonymizeIp() { + return anonymizeIp; + } + + public void setAnonymizeIp(Boolean anonymizeIp) { + this.anonymizeIp = anonymizeIp; + } + + public String getClientName() { + return clientName; + } + + public void setClientName(String clientName) { + this.clientName = clientName; + } + + public String getClientVersion() { + return clientVersion; + } + + public void setClientVersion(String clientVersion) { + this.clientVersion = clientVersion; + } + + public String getProjectId() { + return projectId; + } + + public void setProjectId(String projectId) { + this.projectId = projectId; + } + + public String getRevision() { + return revision; + } + + public void setRevision(String revision) { + this.revision = revision; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + EventBatch that = (EventBatch) o; + + if (!accountId.equals(that.accountId)) return false; + if (!visitors.equals(that.visitors)) return false; + if (anonymizeIp != null ? !anonymizeIp.equals(that.anonymizeIp) : that.anonymizeIp != null) return false; + if (clientName != null ? !clientName.equals(that.clientName) : that.clientName != null) return false; + if (clientVersion != null ? !clientVersion.equals(that.clientVersion) : that.clientVersion != null) + return false; + if (projectId != null ? !projectId.equals(that.projectId) : that.projectId != null) return false; + return revision != null ? revision.equals(that.revision) : that.revision == null; + } + + @Override + public int hashCode() { + int result = accountId.hashCode(); + result = 31 * result + visitors.hashCode(); + result = 31 * result + (anonymizeIp != null ? anonymizeIp.hashCode() : 0); + result = 31 * result + (clientName != null ? clientName.hashCode() : 0); + result = 31 * result + (clientVersion != null ? clientVersion.hashCode() : 0); + result = 31 * result + (projectId != null ? projectId.hashCode() : 0); + result = 31 * result + (revision != null ? revision.hashCode() : 0); + return result; + } +} diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/EventMetric.java b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/EventMetric.java deleted file mode 100644 index f77f7e578..000000000 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/EventMetric.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * - * Copyright 2016-2017, Optimizely and contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.optimizely.ab.event.internal.payload; - -public class EventMetric { - - public static final String REVENUE_METRIC_TYPE = "revenue"; - public static final String NUMERIC_METRIC_TYPE = "value"; - - private String name; - private Number value; - - public EventMetric() { } - - public EventMetric(String name, Number value) { - this.name = name; - this.value = value; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Number getValue() { - return value; - } - - public void setValue(Number value) { - this.value = value; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null || getClass() != obj.getClass()) return false; - - EventMetric that = (EventMetric) obj; - - if (!name.equals(that.name)) return false; - return value.equals(that.value); - } - - @Override - public int hashCode() { - int result = name.hashCode(); - result = 31 * result + value.hashCode(); - return result; - } - - @Override - public String toString() { - return "EventMetric{" + - "name='" + name + '\'' + - ", value=" + value + - '}'; - } -} diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Feature.java b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Feature.java deleted file mode 100644 index 161ee2271..000000000 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Feature.java +++ /dev/null @@ -1,114 +0,0 @@ -/** - * - * Copyright 2016-2017, Optimizely and contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.optimizely.ab.event.internal.payload; - -public class Feature { - - public static final String CUSTOM_ATTRIBUTE_FEATURE_TYPE = "custom"; - public static final String EVENT_FEATURE_TYPE = "custom"; - - private String id; - private String name; - private String type; - private Object value; - private boolean shouldIndex; - - public Feature() { } - - public Feature(String id, String name, String type, Object value, boolean shouldIndex) { - this.id = id; - this.name = name; - this.type = type; - this.value = value; - this.shouldIndex = shouldIndex; - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public Object getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - public boolean getShouldIndex() { - return shouldIndex; - } - - public void setShouldIndex(boolean shouldIndex) { - this.shouldIndex = shouldIndex; - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof Feature)) - return false; - - Feature otherFeature = (Feature)other; - - return id.equals(otherFeature.getId()) && - name.equals(otherFeature.getName()) && - type.equals(otherFeature.getType()) && - value.equals(otherFeature.getValue()) && - shouldIndex == otherFeature.getShouldIndex(); - } - - @Override - public int hashCode() { - int result = id.hashCode(); - result = 31 * result + name.hashCode(); - result = 31 * result + type.hashCode(); - result = 31 * result + value.hashCode(); - result = 31 * result + (shouldIndex ? 1 : 0); - return result; - } - - @Override - public String toString() { - return "FeatureFlag{" + - "id='" + id + '\'' + - ", name='" + name + '\'' + - ", type='" + type + '\'' + - ", value='" + value + '\'' + - ", shouldIndex=" + shouldIndex + - '}'; - } -} diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Impression.java b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Impression.java deleted file mode 100644 index 33878a09f..000000000 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Impression.java +++ /dev/null @@ -1,203 +0,0 @@ -/** - * - * Copyright 2016-2017, Optimizely and contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.optimizely.ab.event.internal.payload; - -import java.util.List; - -public class Impression extends Event { - - private String visitorId; - private long timestamp; - private boolean isGlobalHoldback; - private String projectId; - private Decision decision; - private String layerId; - private String accountId; - private List userFeatures; - private boolean anonymizeIP; - private String sessionId; - private String revision; - - public Impression() { } - - public Impression(String visitorId, long timestamp, boolean isGlobalHoldback, String projectId, Decision decision, - String layerId, String accountId, List userFeatures, boolean anonymizeIP, - String revision) { - this(visitorId, timestamp, isGlobalHoldback, projectId, decision, layerId, accountId, userFeatures, - anonymizeIP, revision, null); - } - - public Impression(String visitorId, long timestamp, boolean isGlobalHoldback, String projectId, Decision decision, - String layerId, String accountId, List userFeatures, boolean anonymizeIP, - String revision, String sessionId) { - this.visitorId = visitorId; - this.timestamp = timestamp; - this.isGlobalHoldback = isGlobalHoldback; - this.projectId = projectId; - this.decision = decision; - this.layerId = layerId; - this.accountId = accountId; - this.userFeatures = userFeatures; - this.anonymizeIP = anonymizeIP; - this.revision = revision; - this.sessionId = sessionId; - } - - public String getVisitorId() { - return visitorId; - } - - public void setVisitorId(String visitorId) { - this.visitorId = visitorId; - } - - public long getTimestamp() { - return timestamp; - } - - public void setTimestamp(long timestamp) { - this.timestamp = timestamp; - } - - public boolean getIsGlobalHoldback() { - return isGlobalHoldback; - } - - public void setIsGlobalHoldback(boolean globalHoldback) { - this.isGlobalHoldback = globalHoldback; - } - - public String getProjectId() { - return projectId; - } - - public void setProjectId(String projectId) { - this.projectId = projectId; - } - - public Decision getDecision() { - return decision; - } - - public void setDecision(Decision decision) { - this.decision = decision; - } - - public String getLayerId() { - return layerId; - } - - public void setLayerId(String layerId) { - this.layerId = layerId; - } - - public String getAccountId() { - return accountId; - } - - public void setAccountId(String accountId) { - this.accountId = accountId; - } - - public List getUserFeatures() { - return userFeatures; - } - - public void setUserFeatures(List userFeatures) { - this.userFeatures = userFeatures; - } - - public boolean getAnonymizeIP() { - return anonymizeIP; - } - - public void setAnonymizeIP(boolean anonymizeIP) { - this.anonymizeIP = anonymizeIP; - } - - public String getSessionId() { - return sessionId; - } - - public void setSessionId(String sessionId) { - this.sessionId = sessionId; - } - - public String getRevision() { - return revision; - } - - public void setRevision(String revision) { - this.revision = revision; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; - - Impression that = (Impression) o; - - if (timestamp != that.timestamp) return false; - if (isGlobalHoldback != that.isGlobalHoldback) return false; - if (anonymizeIP != that.anonymizeIP) return false; - if (!visitorId.equals(that.visitorId)) return false; - if (!projectId.equals(that.projectId)) return false; - if (!decision.equals(that.decision)) return false; - if (!layerId.equals(that.layerId)) return false; - if (!accountId.equals(that.accountId)) return false; - if (!userFeatures.equals(that.userFeatures)) return false; - if (sessionId != null ? !sessionId.equals(that.sessionId) : that.sessionId != null) return false; - return revision.equals(that.revision); - - } - - @Override - public int hashCode() { - int result = super.hashCode(); - result = 31 * result + visitorId.hashCode(); - result = 31 * result + (int) (timestamp ^ (timestamp >>> 32)); - result = 31 * result + (isGlobalHoldback ? 1 : 0); - result = 31 * result + projectId.hashCode(); - result = 31 * result + decision.hashCode(); - result = 31 * result + layerId.hashCode(); - result = 31 * result + accountId.hashCode(); - result = 31 * result + userFeatures.hashCode(); - result = 31 * result + (anonymizeIP ? 1 : 0); - result = 31 * result + (sessionId != null ? sessionId.hashCode() : 0); - result = 31 * result + revision.hashCode(); - return result; - } - - @Override - public String toString() { - return "Impression{" + - "visitorId='" + visitorId + '\'' + - ", timestamp=" + timestamp + - ", isGlobalHoldback=" + isGlobalHoldback + - ", projectId='" + projectId + '\'' + - ", decision=" + decision + - ", layerId='" + layerId + '\'' + - ", accountId='" + accountId + '\'' + - ", userFeatures=" + userFeatures + - ", anonymizeIP=" + anonymizeIP + - ", sessionId='" + sessionId + '\'' + - ", revision='" + revision + '\'' + - '}'; - } -} diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/LayerState.java b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/LayerState.java deleted file mode 100644 index 589197be7..000000000 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/LayerState.java +++ /dev/null @@ -1,89 +0,0 @@ -/** - * - * Copyright 2016-2017, Optimizely and contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.optimizely.ab.event.internal.payload; - -public class LayerState { - - private String layerId; - private String revision; - private Decision decision; - private boolean actionTriggered; - - public LayerState() { } - - public LayerState(String layerId, String revision, Decision decision, boolean actionTriggered) { - this.layerId = layerId; - this.revision = revision; - this.decision = decision; - this.actionTriggered = actionTriggered; - } - - public String getLayerId() { - return layerId; - } - - public void setLayerId(String layerId) { - this.layerId = layerId; - } - - public String getRevision() { - return revision; - } - - public void setRevision(String revision) { - this.revision = revision; - } - - public Decision getDecision() { - return decision; - } - - public void setDecision(Decision decision) { - this.decision = decision; - } - - public boolean getActionTriggered() { - return actionTriggered; - } - - public void setActionTriggered(boolean actionTriggered) { - this.actionTriggered = actionTriggered; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - LayerState that = (LayerState) o; - - if (actionTriggered != that.actionTriggered) return false; - if (!layerId.equals(that.layerId)) return false; - if (!revision.equals(that.revision)) return false; - return decision.equals(that.decision); - - } - - @Override - public int hashCode() { - int result = layerId.hashCode(); - result = 31 * result + revision.hashCode(); - result = 31 * result + decision.hashCode(); - result = 31 * result + (actionTriggered ? 1 : 0); - return result; - } -} diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Snapshot.java b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Snapshot.java new file mode 100644 index 000000000..ee16f6984 --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Snapshot.java @@ -0,0 +1,86 @@ +/** + * + * Copyright 2018, Optimizely and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.optimizely.ab.event.internal.payload; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +public class Snapshot { + List decisions; + List events; + @JsonProperty("activation_timestamp") + Long activationTimestamp; + + public Snapshot() { + + } + + public Snapshot(List decisions, List events) { + this.decisions = decisions; + this.events = events; + this.activationTimestamp = null; + } + + public Long getActivationTimestamp() { + return activationTimestamp; + } + + public void setActivationTimestamp(Long activationTimestamp) { + this.activationTimestamp = activationTimestamp; + } + + public List getDecisions() { + return decisions; + } + + public void setDecisions(List decisions) { + this.decisions = decisions; + } + + public List getEvents() { + return events; + } + + public void setEvents(List events) { + this.events = events; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Snapshot snapshot = (Snapshot) o; + + if (activationTimestamp != null ? + !activationTimestamp.equals(snapshot.activationTimestamp) : + snapshot.activationTimestamp != null) return false; + if (!decisions.equals(snapshot.decisions)) return false; + return events.equals(snapshot.events); + } + + @Override + public int hashCode() { + int result = decisions.hashCode(); + result = 31 * result + events.hashCode(); + if (activationTimestamp != null) { + result = 31 * result + (int) (activationTimestamp ^ (activationTimestamp >>> 32)); + } + return result; + } +} diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Visitor.java b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Visitor.java new file mode 100644 index 000000000..0eb815dbb --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Visitor.java @@ -0,0 +1,95 @@ +/** + * + * Copyright 2018, Optimizely and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.optimizely.ab.event.internal.payload; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +public class Visitor { + @JsonProperty("visitor_id") + String visitorId; + @JsonProperty("session_id") + String sessionId; + List attributes; + List snapshots; + + public Visitor() { + + } + + public Visitor(String visitorId, String sessionId, List attributes, List snapshots) { + this.visitorId = visitorId; + this.sessionId = sessionId; + this.attributes = attributes; + this.snapshots = snapshots; + } + + public String getVisitorId() { + return visitorId; + } + + public void setVisitorId(String visitorId) { + this.visitorId = visitorId; + } + + public String getSessionId() { + return sessionId; + } + + public void setSessionId(String sessionId) { + this.sessionId = sessionId; + } + + public List getAttributes() { + return attributes; + } + + public void setAttributes(List attributes) { + this.attributes = attributes; + } + + public List getSnapshots() { + return snapshots; + } + + public void setSnapshots(List snapshots) { + this.snapshots = snapshots; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Visitor visitor = (Visitor) o; + + if (!visitorId.equals(visitor.visitorId)) return false; + if (sessionId != null ? !sessionId.equals(visitor.sessionId) : visitor.sessionId != null) return false; + if (attributes != null ? !attributes.equals(visitor.attributes) : visitor.attributes != null) return false; + return snapshots.equals(visitor.snapshots); + } + + @Override + public int hashCode() { + int result = visitorId.hashCode(); + result = 31 * result + (sessionId != null ? sessionId.hashCode() : 0); + result = 31 * result + (attributes != null ? attributes.hashCode() : 0); + result = 31 * result + snapshots.hashCode(); + return result; + } +} diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/GsonSerializer.java b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/GsonSerializer.java index a5a7aafba..802e02515 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/GsonSerializer.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/GsonSerializer.java @@ -16,15 +16,17 @@ */ package com.optimizely.ab.event.internal.serializer; +import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; - -import com.optimizely.ab.event.internal.payload.Event; +import com.google.gson.GsonBuilder; class GsonSerializer implements Serializer { - private Gson gson = new Gson(); + private Gson gson = new GsonBuilder() + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + .create(); - public String serialize(T payload) { + public String serialize(T payload) { return gson.toJson(payload); } } diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JacksonSerializer.java b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JacksonSerializer.java index afc316d34..8a42908ce 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JacksonSerializer.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JacksonSerializer.java @@ -19,14 +19,15 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; - -import com.optimizely.ab.event.internal.payload.Event; +import com.fasterxml.jackson.databind.PropertyNamingStrategy; class JacksonSerializer implements Serializer { - private final ObjectMapper mapper = new ObjectMapper(); + private final ObjectMapper mapper = + new ObjectMapper().setPropertyNamingStrategy( + PropertyNamingStrategy.SNAKE_CASE); - public String serialize(T payload) { + public String serialize(T payload) { mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); try { return mapper.writeValueAsString(payload); diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JsonSerializer.java b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JsonSerializer.java index eb1fa71e9..53c8d478a 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JsonSerializer.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JsonSerializer.java @@ -16,13 +16,31 @@ */ package com.optimizely.ab.event.internal.serializer; -import com.optimizely.ab.event.internal.payload.Event; import org.json.JSONObject; class JsonSerializer implements Serializer { - public String serialize(T payload) { + public String serialize(T payload) { JSONObject payloadJsonObject = new JSONObject(payload); - return payloadJsonObject.toString(); + String jsonResponse = payloadJsonObject.toString(); + StringBuilder stringBuilder = new StringBuilder(); + + for (int i = 0; i < jsonResponse.length(); i ++) { + Character ch = jsonResponse.charAt(i); + Character nextChar = null; + if (i +1 < jsonResponse.length()) { + nextChar = jsonResponse.charAt(i+1); + } + if ((Character.isLetter(ch) || Character.isDigit(ch)) && Character.isUpperCase(ch) && + ((Character.isLetter(nextChar) || Character.isDigit(nextChar)))) { + stringBuilder.append('_'); + stringBuilder.append(Character.toLowerCase(ch)); + } + else { + stringBuilder.append(ch); + } + } + + return stringBuilder.toString(); } } diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JsonSimpleSerializer.java b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JsonSimpleSerializer.java index 549fc230e..ea47970f3 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JsonSimpleSerializer.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JsonSimpleSerializer.java @@ -16,142 +16,160 @@ */ package com.optimizely.ab.event.internal.serializer; -import com.optimizely.ab.event.internal.payload.Conversion; +import com.optimizely.ab.event.internal.payload.Attribute; import com.optimizely.ab.event.internal.payload.Decision; -import com.optimizely.ab.event.internal.payload.EventMetric; -import com.optimizely.ab.event.internal.payload.Feature; -import com.optimizely.ab.event.internal.payload.Impression; -import com.optimizely.ab.event.internal.payload.LayerState; -import com.optimizely.ab.event.internal.payload.Event; +import com.optimizely.ab.event.internal.payload.EventBatch; +import com.optimizely.ab.event.internal.payload.Event; +import com.optimizely.ab.event.internal.payload.Snapshot; +import com.optimizely.ab.event.internal.payload.Visitor; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import java.util.List; +import java.util.Map; @SuppressWarnings("unchecked") class JsonSimpleSerializer implements Serializer { - public String serialize(T payload) { - JSONObject payloadJsonObj; - if (payload instanceof Impression) { - payloadJsonObj = serializeImpression((Impression)payload); - } else { - payloadJsonObj = serializeConversion((Conversion)payload); - } + public String serialize(T payload) { + JSONObject payloadJsonObj = serializeEventBatch((EventBatch)payload); return payloadJsonObj.toJSONString(); } - private JSONObject serializeImpression(Impression impression) { + private JSONObject serializeEventBatch(EventBatch eventBatch) { JSONObject jsonObject = new JSONObject(); - jsonObject.put("visitorId", impression.getVisitorId()); - jsonObject.put("timestamp", impression.getTimestamp()); - jsonObject.put("isGlobalHoldback", impression.getIsGlobalHoldback()); - jsonObject.put("anonymizeIP", impression.getAnonymizeIP()); - jsonObject.put("projectId", impression.getProjectId()); - jsonObject.put("decision", serializeDecision(impression.getDecision())); - jsonObject.put("layerId", impression.getLayerId()); - jsonObject.put("accountId", impression.getAccountId()); - jsonObject.put("userFeatures", serializeFeatures(impression.getUserFeatures())); - jsonObject.put("clientEngine", impression.getClientEngine()); - jsonObject.put("clientVersion", impression.getClientVersion()); - jsonObject.put("revision", impression.getRevision()); - - if (impression.getSessionId() != null) { - jsonObject.put("sessionId", impression.getSessionId()); - } + + jsonObject.put("account_id", eventBatch.getAccountId()); + jsonObject.put("visitors", serializeVisitors(eventBatch.getVisitors())); + if (eventBatch.getAnonymizeIp() != null) jsonObject.put("anonymize_ip", eventBatch.getAnonymizeIp()); + if (eventBatch.getClientName() != null) jsonObject.put("client_name", eventBatch.getClientName()); + if (eventBatch.getClientVersion() != null) jsonObject.put("client_version", eventBatch.getClientVersion()); + if (eventBatch.getProjectId() != null) jsonObject.put("project_id", eventBatch.getProjectId()); + if (eventBatch.getRevision() != null) jsonObject.put("revision", eventBatch.getRevision()); return jsonObject; + } - private JSONObject serializeConversion(Conversion conversion) { - JSONObject jsonObject = new JSONObject(); - jsonObject.put("visitorId", conversion.getVisitorId()); - jsonObject.put("timestamp", conversion.getTimestamp()); - jsonObject.put("projectId", conversion.getProjectId()); - jsonObject.put("accountId", conversion.getAccountId()); - jsonObject.put("userFeatures", serializeFeatures(conversion.getUserFeatures())); - jsonObject.put("layerStates", serializeLayerStates(conversion.getLayerStates())); - jsonObject.put("eventEntityId", conversion.getEventEntityId()); - jsonObject.put("eventName", conversion.getEventName()); - jsonObject.put("eventMetrics", serializeEventMetrics(conversion.getEventMetrics())); - jsonObject.put("eventFeatures", serializeFeatures(conversion.getEventFeatures())); - jsonObject.put("isGlobalHoldback", conversion.getIsGlobalHoldback()); - jsonObject.put("anonymizeIP", conversion.getAnonymizeIP()); - jsonObject.put("clientEngine", conversion.getClientEngine()); - jsonObject.put("clientVersion", conversion.getClientVersion()); - jsonObject.put("revision", conversion.getRevision()); - - if (conversion.getSessionId() != null) { - jsonObject.put("sessionId", conversion.getSessionId()); + private JSONArray serializeVisitors(List visitors) { + JSONArray jsonArray = new JSONArray(); + + for (Visitor v: visitors) { + jsonArray.add(serializeVisitor(v)); } + return jsonArray; + } + + private JSONObject serializeVisitor(Visitor visitor) { + JSONObject jsonObject = new JSONObject(); + + jsonObject.put("visitor_id", visitor.getVisitorId()); + + if (visitor.getSessionId() != null) jsonObject.put("session_id", visitor.getSessionId()); + + if (visitor.getAttributes() != null) jsonObject.put("attributes", serializeFeatures(visitor.getAttributes())); + + jsonObject.put("snapshots", serializeSnapshots(visitor.getSnapshots())); + return jsonObject; } - private JSONObject serializeDecision(Decision decision) { + private JSONArray serializeSnapshots(List snapshots) { + JSONArray jsonArray = new JSONArray(); + + for (Snapshot snapshot : snapshots) { + jsonArray.add(serializeSnapshot(snapshot)); + } + + return jsonArray; + } + + private JSONObject serializeSnapshot(Snapshot snapshot) { JSONObject jsonObject = new JSONObject(); - jsonObject.put("variationId", decision.getVariationId()); - jsonObject.put("isLayerHoldback", decision.getIsLayerHoldback()); - jsonObject.put("experimentId", decision.getExperimentId()); + + jsonObject.put("decisions", serializeDecisions(snapshot.getDecisions())); + jsonObject.put("events", serializeEvents(snapshot.getEvents())); return jsonObject; } - private JSONArray serializeFeatures(List features) { + private JSONArray serializeEvents(List events) { JSONArray jsonArray = new JSONArray(); - for (Feature feature : features) { - jsonArray.add(serializeFeature(feature)); + + for (Event event : events) { + jsonArray.add(serializeEvent(event)); } return jsonArray; } - private JSONObject serializeFeature(Feature feature) { + private JSONObject serializeEvent(Event eventV3) { JSONObject jsonObject = new JSONObject(); - jsonObject.put("id", feature.getId()); - jsonObject.put("name", feature.getName()); - jsonObject.put("type", feature.getType()); - jsonObject.put("value", feature.getValue()); - jsonObject.put("shouldIndex", feature.getShouldIndex()); + + jsonObject.put("timestamp",eventV3.getTimestamp()); + jsonObject.put("uuid",eventV3.getUuid()); + jsonObject.put("key", eventV3.getKey()); + + if (eventV3.getEntityId() != null) jsonObject.put("entity_id",eventV3.getEntityId()); + if (eventV3.getQuantity() != null) jsonObject.put("quantity",eventV3.getQuantity()); + if (eventV3.getRevenue() != null) jsonObject.put("revenue",eventV3.getRevenue()); + if (eventV3.getTags() != null) jsonObject.put("tags",serializeTags(eventV3.getTags())); + if (eventV3.getType() != null) jsonObject.put("type",eventV3.getType()); + if (eventV3.getValue() != null) jsonObject.put("value",eventV3.getValue()); return jsonObject; } - private JSONArray serializeLayerStates(List layerStates) { + private JSONArray serializeTags(Map tags) { JSONArray jsonArray = new JSONArray(); - for (LayerState layerState : layerStates) { - jsonArray.add(serializeLayerState(layerState)); + for (Map.Entry entry : tags.entrySet()) { + if (entry.getValue() != null) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put(entry.getKey(), entry.getValue()); + } } return jsonArray; } - private JSONObject serializeLayerState(LayerState layerState) { + private JSONObject serializeDecision(Decision decision) { JSONObject jsonObject = new JSONObject(); - jsonObject.put("layerId", layerState.getLayerId()); - jsonObject.put("revision", layerState.getRevision()); - jsonObject.put("decision", serializeDecision(layerState.getDecision())); - jsonObject.put("actionTriggered", layerState.getActionTriggered()); + jsonObject.put("campaign_id", decision.getCampaignId()); + if (decision.getExperimentId() != null) jsonObject.put("experiment_id", decision.getExperimentId()); + if (decision.getVariationId() != null) jsonObject.put("variation_id", decision.getVariationId()); + jsonObject.put("is_campaign_holdback", decision.getIsCampaignHoldback()); return jsonObject; } - private JSONArray serializeEventMetrics(List eventMetrics) { + private JSONArray serializeFeatures(List features) { JSONArray jsonArray = new JSONArray(); - for (EventMetric eventMetric : eventMetrics) { - jsonArray.add(serializeEventMetric(eventMetric)); + for (Attribute feature : features) { + jsonArray.add(serializeFeature(feature)); } return jsonArray; } - private JSONObject serializeEventMetric(EventMetric eventMetric) { + private JSONObject serializeFeature(Attribute feature) { JSONObject jsonObject = new JSONObject(); - jsonObject.put("name", eventMetric.getName()); - jsonObject.put("value", eventMetric.getValue()); + jsonObject.put("type", feature.getType()); + jsonObject.put("value", feature.getValue()); + if (feature.getEntityId() != null) jsonObject.put("entity_id", feature.getEntityId()); + if (feature.getKey() != null) jsonObject.put("key", feature.getKey()); return jsonObject; } + + private JSONArray serializeDecisions(List layerStates) { + JSONArray jsonArray = new JSONArray(); + for (Decision layerState : layerStates) { + jsonArray.add(serializeDecision(layerState)); + } + + return jsonArray; + } } diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/Serializer.java b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/Serializer.java index 01bd4c123..bc257fa64 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/Serializer.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/Serializer.java @@ -16,8 +16,6 @@ */ package com.optimizely.ab.event.internal.serializer; -import com.optimizely.ab.event.internal.payload.Event; - public interface Serializer { - String serialize(T payload) throws SerializationException; + String serialize(T payload) throws SerializationException; } diff --git a/core-api/src/test/java/com/optimizely/ab/OptimizelyBuilderTest.java b/core-api/src/test/java/com/optimizely/ab/OptimizelyBuilderTest.java index b0ca8ce9a..4a48c7c4b 100644 --- a/core-api/src/test/java/com/optimizely/ab/OptimizelyBuilderTest.java +++ b/core-api/src/test/java/com/optimizely/ab/OptimizelyBuilderTest.java @@ -24,8 +24,8 @@ import com.optimizely.ab.error.NoOpErrorHandler; import com.optimizely.ab.event.EventHandler; import com.optimizely.ab.event.internal.BuildVersionInfo; -import com.optimizely.ab.event.internal.EventBuilderV2; -import com.optimizely.ab.event.internal.payload.Event.ClientEngine; +import com.optimizely.ab.event.internal.EventBuilder; +import com.optimizely.ab.event.internal.payload.EventBatch.ClientEngine; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.junit.BeforeClass; import org.junit.Rule; @@ -129,7 +129,7 @@ public void withDefaultClientEngine() throws Exception { Optimizely optimizelyClient = Optimizely.builder(validConfigJsonV2(), mockEventHandler) .build(); - assertThat(((EventBuilderV2)optimizelyClient.eventBuilder).clientEngine, is(ClientEngine.JAVA_SDK)); + assertThat(((EventBuilder)optimizelyClient.eventBuilder).clientEngine, is(ClientEngine.JAVA_SDK)); } @Test @@ -138,7 +138,7 @@ public void withAndroidSDKClientEngine() throws Exception { .withClientEngine(ClientEngine.ANDROID_SDK) .build(); - assertThat(((EventBuilderV2)optimizelyClient.eventBuilder).clientEngine, is(ClientEngine.ANDROID_SDK)); + assertThat(((EventBuilder)optimizelyClient.eventBuilder).clientEngine, is(ClientEngine.ANDROID_SDK)); } @Test @@ -147,7 +147,7 @@ public void withAndroidTVSDKClientEngine() throws Exception { .withClientEngine(ClientEngine.ANDROID_TV_SDK) .build(); - assertThat(((EventBuilderV2)optimizelyClient.eventBuilder).clientEngine, is(ClientEngine.ANDROID_TV_SDK)); + assertThat(((EventBuilder)optimizelyClient.eventBuilder).clientEngine, is(ClientEngine.ANDROID_TV_SDK)); } @Test @@ -155,7 +155,7 @@ public void withDefaultClientVersion() throws Exception { Optimizely optimizelyClient = Optimizely.builder(validConfigJsonV2(), mockEventHandler) .build(); - assertThat(((EventBuilderV2)optimizelyClient.eventBuilder).clientVersion, is(BuildVersionInfo.VERSION)); + assertThat(((EventBuilder)optimizelyClient.eventBuilder).clientVersion, is(BuildVersionInfo.VERSION)); } @Test @@ -164,7 +164,7 @@ public void withCustomClientVersion() throws Exception { .withClientVersion("0.0.0") .build(); - assertThat(((EventBuilderV2)optimizelyClient.eventBuilder).clientVersion, is("0.0.0")); + assertThat(((EventBuilder)optimizelyClient.eventBuilder).clientVersion, is("0.0.0")); } @SuppressFBWarnings(value="NP_NONNULL_PARAM_VIOLATION", justification="Testing nullness contract violation") diff --git a/core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java b/core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java index 27d213e61..96e929690 100644 --- a/core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java +++ b/core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java @@ -36,7 +36,6 @@ import com.optimizely.ab.event.EventHandler; import com.optimizely.ab.event.LogEvent; import com.optimizely.ab.event.internal.EventBuilder; -import com.optimizely.ab.event.internal.EventBuilderV2; import com.optimizely.ab.internal.LogbackVerifier; import com.optimizely.ab.notification.ActivateNotification; import com.optimizely.ab.notification.NotificationCenter; @@ -101,7 +100,7 @@ import static com.optimizely.ab.config.ValidProjectConfigV4.VARIATION_MULTIVARIATE_EXPERIMENT_GRED; import static com.optimizely.ab.config.ValidProjectConfigV4.VARIATION_MULTIVARIATE_EXPERIMENT_GRED_KEY; import static com.optimizely.ab.event.LogEvent.RequestMethod; -import static com.optimizely.ab.event.internal.EventBuilderV2Test.createExperimentVariationMap; +import static com.optimizely.ab.event.internal.EventBuilderTest.createExperimentVariationMap; import static java.util.Arrays.asList; import static junit.framework.TestCase.assertTrue; import static org.hamcrest.CoreMatchers.is; @@ -1132,7 +1131,7 @@ public void trackEventEndToEndForced() throws Exception { } List allExperiments = new ArrayList(); allExperiments.add(config.getExperiments().get(0)); - EventBuilder eventBuilderV2 = new EventBuilderV2(); + EventBuilder eventBuilder = new EventBuilder(); DecisionService spyDecisionService = spy(new DecisionService(mockBucketer, mockErrorHandler, config, @@ -1140,7 +1139,7 @@ public void trackEventEndToEndForced() throws Exception { Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) .withDecisionService(spyDecisionService) - .withEventBuilder(eventBuilderV2) + .withEventBuilder(eventBuilder) .withConfig(noAudienceProjectConfig) .withErrorHandler(mockErrorHandler) .build(); @@ -1206,7 +1205,7 @@ public void trackEventEndToEnd() throws Exception { } List allExperiments = config.getExperiments(); - EventBuilder eventBuilderV2 = new EventBuilderV2(); + EventBuilder eventBuilder = new EventBuilder(); DecisionService spyDecisionService = spy(new DecisionService(mockBucketer, mockErrorHandler, config, @@ -1214,7 +1213,7 @@ public void trackEventEndToEnd() throws Exception { Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) .withDecisionService(spyDecisionService) - .withEventBuilder(eventBuilderV2) + .withEventBuilder(eventBuilder) .withConfig(noAudienceProjectConfig) .withErrorHandler(mockErrorHandler) .build(); diff --git a/core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderV2Test.java b/core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderTest.java similarity index 65% rename from core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderV2Test.java rename to core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderTest.java index 9eaf419e9..b976bed29 100644 --- a/core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderV2Test.java +++ b/core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderTest.java @@ -1,23 +1,8 @@ -/** - * - * Copyright 2016-2017, Optimizely and contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package com.optimizely.ab.event.internal; +import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; -import com.google.gson.internal.LazilyParsedNumber; +import com.google.gson.GsonBuilder; import com.optimizely.ab.bucketing.Bucketer; import com.optimizely.ab.bucketing.DecisionService; import com.optimizely.ab.bucketing.UserProfileService; @@ -29,19 +14,12 @@ import com.optimizely.ab.error.ErrorHandler; import com.optimizely.ab.error.NoOpErrorHandler; import com.optimizely.ab.event.LogEvent; -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; -import com.optimizely.ab.event.internal.payload.LayerState; +import com.optimizely.ab.event.internal.payload.EventBatch; import com.optimizely.ab.internal.ReservedEventKey; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; import javax.annotation.Nullable; import java.io.IOException; @@ -53,10 +31,6 @@ import java.util.List; import java.util.Map; -import static com.optimizely.ab.config.ProjectConfigTestUtils.noAudienceProjectConfigJsonV2; -import static com.optimizely.ab.config.ProjectConfigTestUtils.noAudienceProjectConfigV2; -import static com.optimizely.ab.config.ProjectConfigTestUtils.validConfigJsonV2; -import static com.optimizely.ab.config.ProjectConfigTestUtils.validConfigJsonV4; import static com.optimizely.ab.config.ProjectConfigTestUtils.validProjectConfigV2; import static com.optimizely.ab.config.ProjectConfigTestUtils.validProjectConfigV4; import static com.optimizely.ab.config.ValidProjectConfigV4.AUDIENCE_GRYFFINDOR_VALUE; @@ -64,7 +38,7 @@ import static com.optimizely.ab.config.ValidProjectConfigV4.EVENT_PAUSED_EXPERIMENT_KEY; import static com.optimizely.ab.config.ValidProjectConfigV4.MULTIVARIATE_EXPERIMENT_FORCED_VARIATION_USER_ID_GRED; import static com.optimizely.ab.config.ValidProjectConfigV4.PAUSED_EXPERIMENT_FORCED_VARIATION_USER_ID_CONTROL; -import static junit.framework.TestCase.assertNotNull; +import static junit.framework.Assert.assertNotNull; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.containsInAnyOrder; @@ -79,13 +53,10 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -/** - * Test for {@link EventBuilderV2} - */ @RunWith(Parameterized.class) -public class EventBuilderV2Test { +public class EventBuilderTest { - @Parameters + @Parameterized.Parameters public static Collection data() throws IOException { return Arrays.asList(new Object[][] { { @@ -99,21 +70,23 @@ public static Collection data() throws IOException { }); } - private Gson gson = new Gson(); - private EventBuilderV2 builder = new EventBuilderV2(); + private Gson gson = new GsonBuilder() + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + .create(); + private EventBuilder builder = new EventBuilder(); private static String userId = "userId"; private int datafileVersion; private ProjectConfig validProjectConfig; - public EventBuilderV2Test(int datafileVersion, - ProjectConfig validProjectConfig) { + public EventBuilderTest(int datafileVersion, + ProjectConfig validProjectConfig) { this.datafileVersion = datafileVersion; this.validProjectConfig = validProjectConfig; } /** - * Verify {@link Impression} event creation + * Verify {@link com.optimizely.ab.event.internal.payload.EventBatch} event creation */ @Test public void createImpressionEvent() throws Exception { @@ -124,32 +97,34 @@ public void createImpressionEvent() throws Exception { Attribute attribute = projectConfig.getAttributes().get(0); String userId = "userId"; Map attributeMap = Collections.singletonMap(attribute.getKey(), "value"); - Decision expectedDecision = new Decision(bucketedVariation.getId(), false, activatedExperiment.getId()); - Feature feature = new Feature(attribute.getId(), attribute.getKey(), Feature.CUSTOM_ATTRIBUTE_FEATURE_TYPE, - "value", true); - List expectedUserFeatures = Collections.singletonList(feature); + Decision expectedDecision = new Decision(activatedExperiment.getLayerId(), activatedExperiment.getId(), bucketedVariation.getId(), false); + com.optimizely.ab.event.internal.payload.Attribute feature = new com.optimizely.ab.event.internal.payload.Attribute(attribute.getId(), + attribute.getKey(), com.optimizely.ab.event.internal.payload.Attribute.CUSTOM_ATTRIBUTE_TYPE, + "value"); + List expectedUserFeatures = Collections.singletonList(feature); LogEvent impressionEvent = builder.createImpressionEvent(projectConfig, activatedExperiment, bucketedVariation, - userId, attributeMap); + userId, attributeMap); // verify that request endpoint is correct - assertThat(impressionEvent.getEndpointUrl(), is(EventBuilderV2.IMPRESSION_ENDPOINT)); + assertThat(impressionEvent.getEndpointUrl(), is(EventBuilder.EVENT_ENDPOINT)); - Impression impression = gson.fromJson(impressionEvent.getBody(), Impression.class); + EventBatch eventBatch = gson.fromJson(impressionEvent.getBody(), EventBatch.class); // verify payload information - assertThat(impression.getVisitorId(), is(userId)); - assertThat((double)impression.getTimestamp(), closeTo((double)System.currentTimeMillis(), 60.0)); - assertFalse(impression.getIsGlobalHoldback()); - assertThat(impression.getAnonymizeIP(), is(projectConfig.getAnonymizeIP())); - assertThat(impression.getProjectId(), is(projectConfig.getProjectId())); - assertThat(impression.getDecision(), is(expectedDecision)); - assertThat(impression.getLayerId(), is(activatedExperiment.getLayerId())); - assertThat(impression.getAccountId(), is(projectConfig.getAccountId())); - assertThat(impression.getUserFeatures(), is(expectedUserFeatures)); - assertThat(impression.getClientEngine(), is(ClientEngine.JAVA_SDK.getClientEngineValue())); - assertThat(impression.getClientVersion(), is(BuildVersionInfo.VERSION)); - assertNull(impression.getSessionId()); + assertThat(eventBatch.getVisitors().get(0).getVisitorId(), is(userId)); + assertThat((double) eventBatch.getVisitors().get(0).getSnapshots().get(0).getEvents().get(0).getTimestamp(), closeTo((double)System.currentTimeMillis(), 1000.0)); + assertFalse(eventBatch.getVisitors().get(0).getSnapshots().get(0).getDecisions().get(0).getIsCampaignHoldback()); + assertThat(eventBatch.getAnonymizeIp(), is(projectConfig.getAnonymizeIP())); + assertThat(eventBatch.getProjectId(), is(projectConfig.getProjectId())); + assertThat(eventBatch.getVisitors().get(0).getSnapshots().get(0).getDecisions().get(0), is(expectedDecision)); + assertThat(eventBatch.getVisitors().get(0).getSnapshots().get(0).getDecisions().get(0).getCampaignId(), + is(activatedExperiment.getLayerId())); + assertThat(eventBatch.getAccountId(), is(projectConfig.getAccountId())); + assertThat(eventBatch.getVisitors().get(0).getAttributes(), is(expectedUserFeatures)); + assertThat(eventBatch.getClientName(), is(EventBatch.ClientEngine.JAVA_SDK.getClientEngineValue())); + assertThat(eventBatch.getClientVersion(), is(BuildVersionInfo.VERSION)); + assertNull(eventBatch.getVisitors().get(0).getSessionId()); } /** @@ -165,24 +140,24 @@ public void createImpressionEventIgnoresUnknownAttributes() throws Exception { LogEvent impressionEvent = builder.createImpressionEvent(projectConfig, activatedExperiment, bucketedVariation, "userId", - Collections.singletonMap("unknownAttribute", "blahValue")); + Collections.singletonMap("unknownAttribute", "blahValue")); - Impression impression = gson.fromJson(impressionEvent.getBody(), Impression.class); + EventBatch impression = gson.fromJson(impressionEvent.getBody(), EventBatch.class); // verify that no Feature is created for "unknownAtrribute" -> "blahValue" - for (Feature feature : impression.getUserFeatures()) { - assertFalse(feature.getName() == "unknownAttribute"); + for (com.optimizely.ab.event.internal.payload.Attribute feature : impression.getVisitors().get(0).getAttributes()) { + assertFalse(feature.getKey() == "unknownAttribute"); assertFalse(feature.getValue() == "blahValue"); } } /** - * Verify that supplying {@link EventBuilderV2} with a custom client engine and client version results in impression + * Verify that supplying {@link EventBuilder} with a custom client engine and client version results in impression * events being sent with the overriden values. */ @Test public void createImpressionEventAndroidClientEngineClientVersion() throws Exception { - EventBuilderV2 builder = new EventBuilderV2(ClientEngine.ANDROID_SDK, "0.0.0"); + EventBuilder builder = new EventBuilder(EventBatch.ClientEngine.ANDROID_SDK, "0.0.0"); ProjectConfig projectConfig = validProjectConfigV2(); Experiment activatedExperiment = projectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); @@ -191,21 +166,21 @@ public void createImpressionEventAndroidClientEngineClientVersion() throws Excep Map attributeMap = Collections.singletonMap(attribute.getKey(), "value"); LogEvent impressionEvent = builder.createImpressionEvent(projectConfig, activatedExperiment, bucketedVariation, - userId, attributeMap); - Impression impression = gson.fromJson(impressionEvent.getBody(), Impression.class); + userId, attributeMap); + EventBatch impression = gson.fromJson(impressionEvent.getBody(), EventBatch.class); - assertThat(impression.getClientEngine(), is(ClientEngine.ANDROID_SDK.getClientEngineValue())); + assertThat(impression.getClientName(), is(EventBatch.ClientEngine.ANDROID_SDK.getClientEngineValue())); assertThat(impression.getClientVersion(), is("0.0.0")); } /** - * Verify that supplying {@link EventBuilderV2} with a custom Android TV client engine and client version + * Verify that supplying {@link EventBuilder} with a custom Android TV client engine and client version * results in impression events being sent with the overriden values. */ @Test public void createImpressionEventAndroidTVClientEngineClientVersion() throws Exception { String clientVersion = "0.0.0"; - EventBuilderV2 builder = new EventBuilderV2(ClientEngine.ANDROID_TV_SDK, clientVersion); + EventBuilder builder = new EventBuilder(EventBatch.ClientEngine.ANDROID_TV_SDK, clientVersion); ProjectConfig projectConfig = validProjectConfigV2(); Experiment activatedExperiment = projectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); @@ -214,15 +189,15 @@ public void createImpressionEventAndroidTVClientEngineClientVersion() throws Exc Map attributeMap = Collections.singletonMap(attribute.getKey(), "value"); LogEvent impressionEvent = builder.createImpressionEvent(projectConfig, activatedExperiment, bucketedVariation, - userId, attributeMap); - Impression impression = gson.fromJson(impressionEvent.getBody(), Impression.class); + userId, attributeMap); + EventBatch impression = gson.fromJson(impressionEvent.getBody(), EventBatch.class); - assertThat(impression.getClientEngine(), is(ClientEngine.ANDROID_TV_SDK.getClientEngineValue())); + assertThat(impression.getClientName(), is(EventBatch.ClientEngine.ANDROID_TV_SDK.getClientEngineValue())); assertThat(impression.getClientVersion(), is(clientVersion)); } /** - * Verify {@link Conversion} event creation + * Verify {@link com.optimizely.ab.event.internal.payload.EventBatch} event creation */ @Test public void createConversionEvent() throws Exception { @@ -268,53 +243,48 @@ public void createConversionEvent() throws Exception { attributeMap, eventTagMap); - List expectedLayerStates = new ArrayList(); + List expectedDecisions = new ArrayList(); for (Experiment experiment : experimentsForEventKey) { if (experiment.isRunning()) { - LayerState layerState = new LayerState(experiment.getLayerId(), validProjectConfig.getRevision(), - new Decision(experiment.getVariations().get(0).getId(), false, experiment.getId()), true); - expectedLayerStates.add(layerState); + Decision layerState = new Decision(experiment.getLayerId(), experiment.getId(), + experiment.getVariations().get(0).getId(), false); + expectedDecisions.add(layerState); } } // verify that the request endpoint is correct - assertThat(conversionEvent.getEndpointUrl(), is(EventBuilderV2.CONVERSION_ENDPOINT)); + assertThat(conversionEvent.getEndpointUrl(), is(EventBuilder.EVENT_ENDPOINT)); - Conversion conversion = gson.fromJson(conversionEvent.getBody(), Conversion.class); + EventBatch conversion = gson.fromJson(conversionEvent.getBody(), EventBatch.class); // verify payload information - assertThat(conversion.getVisitorId(), is(userId)); - assertThat((double)conversion.getTimestamp(), closeTo((double)System.currentTimeMillis(), 120.0)); + assertThat(conversion.getVisitors().get(0).getVisitorId(), is(userId)); + assertThat((double)conversion.getVisitors().get(0).getSnapshots().get(0).getEvents().get(0).getTimestamp(), + closeTo((double)System.currentTimeMillis(), 120.0)); assertThat(conversion.getProjectId(), is(validProjectConfig.getProjectId())); assertThat(conversion.getAccountId(), is(validProjectConfig.getAccountId())); - Feature feature = new Feature(attribute.getId(), attribute.getKey(), Feature.CUSTOM_ATTRIBUTE_FEATURE_TYPE, - AUDIENCE_GRYFFINDOR_VALUE, true); - List expectedUserFeatures = Collections.singletonList(feature); - - // Event Features - List expectedEventFeatures = new ArrayList(); - expectedEventFeatures.add(new Feature("", "boolean_param", Feature.EVENT_FEATURE_TYPE, - false, false)); - expectedEventFeatures.add(new Feature("", "string_param", Feature.EVENT_FEATURE_TYPE, - "123", false)); - - assertEquals(conversion.getUserFeatures(), expectedUserFeatures); - assertThat(conversion.getLayerStates(), containsInAnyOrder(expectedLayerStates.toArray())); - assertEquals(conversion.getEventEntityId(), eventType.getId()); - assertEquals(conversion.getEventName(), eventType.getKey()); - assertEquals(conversion.getEventMetrics(), Collections.emptyList()); - assertTrue(conversion.getEventFeatures().containsAll(expectedEventFeatures)); - assertTrue(expectedEventFeatures.containsAll(conversion.getEventFeatures())); - assertFalse(conversion.getIsGlobalHoldback()); - assertEquals(conversion.getAnonymizeIP(), validProjectConfig.getAnonymizeIP()); - assertEquals(conversion.getClientEngine(), ClientEngine.JAVA_SDK.getClientEngineValue()); + com.optimizely.ab.event.internal.payload.Attribute feature = new com.optimizely.ab.event.internal.payload.Attribute(attribute.getId(), attribute.getKey(), + com.optimizely.ab.event.internal.payload.Attribute.CUSTOM_ATTRIBUTE_TYPE, + AUDIENCE_GRYFFINDOR_VALUE); + List expectedUserFeatures = Collections.singletonList(feature); + + assertEquals(conversion.getVisitors().get(0).getAttributes(), expectedUserFeatures); + assertThat(conversion.getVisitors().get(0).getSnapshots().get(0).getDecisions(), containsInAnyOrder(expectedDecisions.toArray())); + assertEquals(conversion.getVisitors().get(0).getSnapshots().get(0).getEvents().get(0).getEntityId(), eventType.getId()); + assertEquals(conversion.getVisitors().get(0).getSnapshots().get(0).getEvents().get(0).getKey(), eventType.getKey()); + assertEquals(conversion.getVisitors().get(0).getSnapshots().get(0).getEvents().get(0).getRevenue(), null); + assertTrue(conversion.getVisitors().get(0).getAttributes().containsAll(expectedUserFeatures)); + assertTrue(conversion.getVisitors().get(0).getSnapshots().get(0).getEvents().get(0).getTags().equals(eventTagMap)); + assertFalse(conversion.getVisitors().get(0).getSnapshots().get(0).getDecisions().get(0).getIsCampaignHoldback()); + assertEquals(conversion.getAnonymizeIp(), validProjectConfig.getAnonymizeIP()); + assertEquals(conversion.getClientName(), EventBatch.ClientEngine.JAVA_SDK.getClientEngineValue()); assertEquals(conversion.getClientVersion(), BuildVersionInfo.VERSION); } /** - * Verify that "revenue" and "value" are properly recorded in a conversion request as {@link EventMetric} objects. + * Verify that "revenue" and "value" are properly recorded in a conversion request as {@link com.optimizely.ab.event.internal.payload.Event} objects. * "revenue" is fixed-point and "value" is floating-point. */ @Test @@ -354,12 +324,10 @@ public void createConversionParamsWithEventMetrics() throws Exception { eventType.getId(), eventType.getKey(), attributeMap, eventTagMap); - Conversion conversion = gson.fromJson(conversionEvent.getBody(), Conversion.class); - List eventMetrics = Arrays.asList( - new EventMetric(EventMetric.REVENUE_METRIC_TYPE, new LazilyParsedNumber(revenue.toString())), - new EventMetric(EventMetric.NUMERIC_METRIC_TYPE, new LazilyParsedNumber(value.toString()))); + EventBatch conversion = gson.fromJson(conversionEvent.getBody(), EventBatch.class); // we're not going to verify everything, only the event metrics - assertThat(conversion.getEventMetrics(), is(eventMetrics)); + assertThat(conversion.getVisitors().get(0).getSnapshots().get(0).getEvents().get(0).getRevenue().longValue(), is(revenue)); + assertThat(conversion.getVisitors().get(0).getSnapshots().get(0).getEvents().get(0).getValue().doubleValue(), is(value)); } /** @@ -404,15 +372,15 @@ public void createConversionEventForcedVariationBucketingPrecedesAudienceEval() Collections.emptyMap()); assertNotNull(conversionEvent); - Conversion conversion = gson.fromJson(conversionEvent.getBody(), Conversion.class); + EventBatch conversion = gson.fromJson(conversionEvent.getBody(), EventBatch.class); if (datafileVersion == 4) { // 2 experiments use the event // basic experiment has no audience // user is whitelisted in to one audience - assertEquals(2, conversion.getLayerStates().size()); + assertEquals(2, conversion.getVisitors().get(0).getSnapshots().get(0).getDecisions().size()); } else { - assertEquals(1, conversion.getLayerStates().size()); + assertEquals(1, conversion.getVisitors().get(0).getSnapshots().get(0).getDecisions().size()); } } @@ -462,12 +430,12 @@ public void createConversionEventExperimentStatusPrecedesForcedVariation() { } /** - * Verify that supplying {@link EventBuilderV2} with a custom client engine and client version results in conversion + * Verify that supplying {@link EventBuilder} with a custom client engine and client version results in conversion * events being sent with the overriden values. */ @Test public void createConversionEventAndroidClientEngineClientVersion() throws Exception { - EventBuilderV2 builder = new EventBuilderV2(ClientEngine.ANDROID_SDK, "0.0.0"); + EventBuilder builder = new EventBuilder(EventBatch.ClientEngine.ANDROID_SDK, "0.0.0"); Attribute attribute = validProjectConfig.getAttributes().get(0); EventType eventType = validProjectConfig.getEventTypes().get(0); @@ -499,20 +467,20 @@ public void createConversionEventAndroidClientEngineClientVersion() throws Excep attributeMap, Collections.emptyMap()); - Conversion conversion = gson.fromJson(conversionEvent.getBody(), Conversion.class); + EventBatch conversion = gson.fromJson(conversionEvent.getBody(), EventBatch.class); - assertThat(conversion.getClientEngine(), is(ClientEngine.ANDROID_SDK.getClientEngineValue())); + assertThat(conversion.getClientName(), is(EventBatch.ClientEngine.ANDROID_SDK.getClientEngineValue())); assertThat(conversion.getClientVersion(), is("0.0.0")); } /** - * Verify that supplying {@link EventBuilderV2} with a Android TV client engine and client version results in + * Verify that supplying {@link EventBuilder} with a Android TV client engine and client version results in * conversion events being sent with the overriden values. */ @Test public void createConversionEventAndroidTVClientEngineClientVersion() throws Exception { String clientVersion = "0.0.0"; - EventBuilderV2 builder = new EventBuilderV2(ClientEngine.ANDROID_TV_SDK, clientVersion); + EventBuilder builder = new EventBuilder(EventBatch.ClientEngine.ANDROID_TV_SDK, clientVersion); ProjectConfig projectConfig = validProjectConfigV2(); Attribute attribute = projectConfig.getAttributes().get(0); EventType eventType = projectConfig.getEventTypes().get(0); @@ -539,21 +507,21 @@ public void createConversionEventAndroidTVClientEngineClientVersion() throws Exc eventType.getKey(), attributeMap, Collections.emptyMap()); - Conversion conversion = gson.fromJson(conversionEvent.getBody(), Conversion.class); + EventBatch conversion = gson.fromJson(conversionEvent.getBody(), EventBatch.class); - assertThat(conversion.getClientEngine(), is(ClientEngine.ANDROID_TV_SDK.getClientEngineValue())); + assertThat(conversion.getClientName(), is(EventBatch.ClientEngine.ANDROID_TV_SDK.getClientEngineValue())); assertThat(conversion.getClientVersion(), is(clientVersion)); } /** * Verify that supplying an empty Experiment Variation map to - * {@link EventBuilderV2#createConversionEvent(ProjectConfig, Map, String, String, String, Map, Map)} + * {@link EventBuilder#createConversionEvent(ProjectConfig, Map, String, String, String, Map, Map)} * returns a null {@link LogEvent}. */ @Test public void createConversionEventReturnsNullWhenExperimentVariationMapIsEmpty() { EventType eventType = validProjectConfig.getEventTypes().get(0); - EventBuilderV2 builder = new EventBuilderV2(); + EventBuilder builder = new EventBuilder(); LogEvent conversionEvent = builder.createConversionEvent( validProjectConfig, @@ -569,7 +537,7 @@ public void createConversionEventReturnsNullWhenExperimentVariationMapIsEmpty() } /** - * Verify {@link Impression} event creation + * Verify {@link com.optimizely.ab.event.internal.payload.EventBatch} event creation */ @Test public void createImpressionEventWithBucketingId() throws Exception { @@ -584,43 +552,44 @@ public void createImpressionEventWithBucketingId() throws Exception { attributeMap.put(com.optimizely.ab.bucketing.DecisionService.BUCKETING_ATTRIBUTE, "variation"); - Decision expectedDecision = new Decision(bucketedVariation.getId(), false, activatedExperiment.getId()); - Feature feature = new Feature(attribute.getId(), attribute.getKey(), Feature.CUSTOM_ATTRIBUTE_FEATURE_TYPE, - "value", true); - Feature feature1 = new Feature(com.optimizely.ab.bucketing.DecisionService.BUCKETING_ATTRIBUTE, - com.optimizely.ab.event.internal.EventBuilderV2.ATTRIBUTE_KEY_FOR_BUCKETING_ATTRIBUTE, - Feature.CUSTOM_ATTRIBUTE_FEATURE_TYPE, - "variation", true); - List expectedUserFeatures = new java.util.ArrayList(); - expectedUserFeatures.add(feature); - expectedUserFeatures.add(feature1); + Decision expectedDecision = new Decision(activatedExperiment.getLayerId(), activatedExperiment.getId(), bucketedVariation.getId(), false); + + com.optimizely.ab.event.internal.payload.Attribute feature = new com.optimizely.ab.event.internal.payload.Attribute(attribute.getId(), attribute.getKey(), + com.optimizely.ab.event.internal.payload.Attribute.CUSTOM_ATTRIBUTE_TYPE, + "value"); + com.optimizely.ab.event.internal.payload.Attribute feature1 = new com.optimizely.ab.event.internal.payload.Attribute(com.optimizely.ab.bucketing.DecisionService.BUCKETING_ATTRIBUTE, + EventBuilder.ATTRIBUTE_KEY_FOR_BUCKETING_ATTRIBUTE, + com.optimizely.ab.event.internal.payload.Attribute.CUSTOM_ATTRIBUTE_TYPE, + "variation"); + + List expectedUserFeatures = Arrays.asList(feature, feature1); LogEvent impressionEvent = builder.createImpressionEvent(projectConfig, activatedExperiment, bucketedVariation, userId, attributeMap); // verify that request endpoint is correct - assertThat(impressionEvent.getEndpointUrl(), is(EventBuilderV2.IMPRESSION_ENDPOINT)); + assertThat(impressionEvent.getEndpointUrl(), is(EventBuilder.EVENT_ENDPOINT)); - Impression impression = gson.fromJson(impressionEvent.getBody(), Impression.class); + EventBatch impression = gson.fromJson(impressionEvent.getBody(), EventBatch.class); // verify payload information - assertThat(impression.getVisitorId(), is(userId)); - assertThat((double)impression.getTimestamp(), closeTo((double)System.currentTimeMillis(), 60.0)); - assertFalse(impression.getIsGlobalHoldback()); - assertThat(impression.getAnonymizeIP(), is(projectConfig.getAnonymizeIP())); + assertThat(impression.getVisitors().get(0).getVisitorId(), is(userId)); + assertThat((double)impression.getVisitors().get(0).getSnapshots().get(0).getEvents().get(0).getTimestamp(), closeTo((double)System.currentTimeMillis(), 1000.0)); + assertFalse(impression.getVisitors().get(0).getSnapshots().get(0).getDecisions().get(0).getIsCampaignHoldback()); + assertThat(impression.getAnonymizeIp(), is(projectConfig.getAnonymizeIP())); assertThat(impression.getProjectId(), is(projectConfig.getProjectId())); - assertThat(impression.getDecision(), is(expectedDecision)); - assertThat(impression.getLayerId(), is(activatedExperiment.getLayerId())); + assertThat(impression.getVisitors().get(0).getSnapshots().get(0).getDecisions().get(0), is(expectedDecision)); + assertThat(impression.getVisitors().get(0).getSnapshots().get(0).getDecisions().get(0).getCampaignId(), is(activatedExperiment.getLayerId())); assertThat(impression.getAccountId(), is(projectConfig.getAccountId())); - assertThat(impression.getUserFeatures(), is(expectedUserFeatures)); - assertThat(impression.getClientEngine(), is(ClientEngine.JAVA_SDK.getClientEngineValue())); + assertThat(impression.getVisitors().get(0).getAttributes(), is(expectedUserFeatures)); + assertThat(impression.getClientName(), is(EventBatch.ClientEngine.JAVA_SDK.getClientEngineValue())); assertThat(impression.getClientVersion(), is(BuildVersionInfo.VERSION)); - assertNull(impression.getSessionId()); + assertNull(impression.getVisitors().get(0).getSessionId()); } /** - * Verify {@link Conversion} event creation + * Verify {@link EventBatch} event creation */ @Test public void createConversionEventWithBucketingId() throws Exception { @@ -670,54 +639,47 @@ public void createConversionEventWithBucketingId() throws Exception { attributeMap, eventTagMap); - List expectedLayerStates = new ArrayList(); + List expectedDecisions = new ArrayList(); for (Experiment experiment : experimentsForEventKey) { if (experiment.isRunning()) { - LayerState layerState = new LayerState(experiment.getLayerId(), validProjectConfig.getRevision(), - new Decision(experiment.getVariations().get(0).getId(), false, experiment.getId()), true); - expectedLayerStates.add(layerState); + Decision decision = new Decision(experiment.getLayerId(), experiment.getId(), + experiment.getVariations().get(0).getId(), false); + expectedDecisions.add(decision); } } // verify that the request endpoint is correct - assertThat(conversionEvent.getEndpointUrl(), is(EventBuilderV2.CONVERSION_ENDPOINT)); + assertThat(conversionEvent.getEndpointUrl(), is(EventBuilder.EVENT_ENDPOINT)); - Conversion conversion = gson.fromJson(conversionEvent.getBody(), Conversion.class); + EventBatch conversion = gson.fromJson(conversionEvent.getBody(), EventBatch.class); // verify payload information - assertThat(conversion.getVisitorId(), is(userId)); - assertThat((double)conversion.getTimestamp(), closeTo((double)System.currentTimeMillis(), 120.0)); + assertThat(conversion.getVisitors().get(0).getVisitorId(), is(userId)); + assertThat((double)conversion.getVisitors().get(0).getSnapshots().get(0).getEvents().get(0).getTimestamp(), closeTo((double)System.currentTimeMillis(), 1000.0)); assertThat(conversion.getProjectId(), is(validProjectConfig.getProjectId())); assertThat(conversion.getAccountId(), is(validProjectConfig.getAccountId())); - Feature feature = new Feature(attribute.getId(), attribute.getKey(), Feature.CUSTOM_ATTRIBUTE_FEATURE_TYPE, - AUDIENCE_GRYFFINDOR_VALUE, true); - Feature feature1 = new Feature(com.optimizely.ab.bucketing.DecisionService.BUCKETING_ATTRIBUTE, - com.optimizely.ab.event.internal.EventBuilderV2.ATTRIBUTE_KEY_FOR_BUCKETING_ATTRIBUTE, - Feature.CUSTOM_ATTRIBUTE_FEATURE_TYPE, - bucketingId, true); - List expectedUserFeatures = new ArrayList(); - expectedUserFeatures.add(feature); - expectedUserFeatures.add(feature1); - - // Event Features - List expectedEventFeatures = new ArrayList(); - expectedEventFeatures.add(new Feature("", "boolean_param", Feature.EVENT_FEATURE_TYPE, - false, false)); - expectedEventFeatures.add(new Feature("", "string_param", Feature.EVENT_FEATURE_TYPE, - "123", false)); - - assertEquals(conversion.getUserFeatures(), expectedUserFeatures); - assertThat(conversion.getLayerStates(), containsInAnyOrder(expectedLayerStates.toArray())); - assertEquals(conversion.getEventEntityId(), eventType.getId()); - assertEquals(conversion.getEventName(), eventType.getKey()); - assertEquals(conversion.getEventMetrics(), Collections.emptyList()); - assertTrue(conversion.getEventFeatures().containsAll(expectedEventFeatures)); - assertTrue(expectedEventFeatures.containsAll(conversion.getEventFeatures())); - assertFalse(conversion.getIsGlobalHoldback()); - assertEquals(conversion.getAnonymizeIP(), validProjectConfig.getAnonymizeIP()); - assertEquals(conversion.getClientEngine(), ClientEngine.JAVA_SDK.getClientEngineValue()); + com.optimizely.ab.event.internal.payload.Attribute attribute1 = new com.optimizely.ab.event.internal.payload.Attribute(attribute.getId(), attribute.getKey(), + com.optimizely.ab.event.internal.payload.Attribute.CUSTOM_ATTRIBUTE_TYPE, + AUDIENCE_GRYFFINDOR_VALUE); + com.optimizely.ab.event.internal.payload.Attribute attribute2 = new com.optimizely.ab.event.internal.payload.Attribute(com.optimizely.ab.bucketing.DecisionService.BUCKETING_ATTRIBUTE, + EventBuilder.ATTRIBUTE_KEY_FOR_BUCKETING_ATTRIBUTE, + com.optimizely.ab.event.internal.payload.Attribute.CUSTOM_ATTRIBUTE_TYPE, + bucketingId); + List expectedUserFeatures = Arrays.asList(attribute1, attribute2); + + assertEquals(conversion.getVisitors().get(0).getAttributes(), expectedUserFeatures); + assertThat(conversion.getVisitors().get(0).getSnapshots().get(0).getDecisions(), containsInAnyOrder(expectedDecisions.toArray())); + assertEquals(conversion.getVisitors().get(0).getSnapshots().get(0).getEvents().get(0).getEntityId(), eventType.getId()); + assertEquals(conversion.getVisitors().get(0).getSnapshots().get(0).getEvents().get(0).getType(), eventType.getKey()); + assertEquals(conversion.getVisitors().get(0).getSnapshots().get(0).getEvents().get(0).getKey(), eventType.getKey()); + assertEquals(conversion.getVisitors().get(0).getSnapshots().get(0).getEvents().get(0).getRevenue(), null); + assertEquals(conversion.getVisitors().get(0).getSnapshots().get(0).getEvents().get(0).getQuantity(), null); + assertTrue(conversion.getVisitors().get(0).getSnapshots().get(0).getEvents().get(0).getTags().equals(eventTagMap)); + assertFalse(conversion.getVisitors().get(0).getSnapshots().get(0).getDecisions().get(0).getIsCampaignHoldback()); + assertEquals(conversion.getAnonymizeIp(), validProjectConfig.getAnonymizeIP()); + assertEquals(conversion.getClientName(), EventBatch.ClientEngine.JAVA_SDK.getClientEngineValue()); assertEquals(conversion.getClientVersion(), BuildVersionInfo.VERSION); } @@ -743,3 +705,4 @@ public static Map createExperimentVariationMap(ProjectCon return experimentVariationMap; } } + diff --git a/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/GsonSerializerTest.java b/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/GsonSerializerTest.java index a69340482..4581225a8 100644 --- a/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/GsonSerializerTest.java +++ b/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/GsonSerializerTest.java @@ -16,10 +16,11 @@ */ package com.optimizely.ab.event.internal.serializer; +import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; -import com.optimizely.ab.event.internal.payload.Conversion; -import com.optimizely.ab.event.internal.payload.Impression; +import com.google.gson.GsonBuilder; +import com.optimizely.ab.event.internal.payload.EventBatch; import org.junit.Test; @@ -40,44 +41,46 @@ public class GsonSerializerTest { private GsonSerializer serializer = new GsonSerializer(); - private Gson gson = new Gson(); + private Gson gson = new GsonBuilder() + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + .create(); @Test public void serializeImpression() throws IOException { - Impression impression = generateImpression(); + EventBatch impression = generateImpression(); // can't compare JSON strings since orders could vary so compare objects instead - Impression actual = gson.fromJson(serializer.serialize(impression), Impression.class); - Impression expected = gson.fromJson(generateImpressionJson(), Impression.class); + EventBatch actual = gson.fromJson(serializer.serialize(impression), EventBatch.class); + EventBatch expected = gson.fromJson(generateImpressionJson(), EventBatch.class); assertThat(actual, is(expected)); } @Test public void serializeImpressionWithSessionId() throws IOException { - Impression impression = generateImpressionWithSessionId(); + EventBatch impression = generateImpressionWithSessionId(); // can't compare JSON strings since orders could vary so compare objects instead - Impression actual = gson.fromJson(serializer.serialize(impression), Impression.class); - Impression expected = gson.fromJson(generateImpressionWithSessionIdJson(), Impression.class); + EventBatch actual = gson.fromJson(serializer.serialize(impression), EventBatch.class); + EventBatch expected = gson.fromJson(generateImpressionWithSessionIdJson(), EventBatch.class); assertThat(actual, is(expected)); } @Test public void serializeConversion() throws IOException { - Conversion conversion = generateConversion(); + EventBatch conversion = generateConversion(); // can't compare JSON strings since orders could vary so compare objects instead - Conversion actual = gson.fromJson(serializer.serialize(conversion), Conversion.class); - Conversion expected = gson.fromJson(generateConversionJson(), Conversion.class); + EventBatch actual = gson.fromJson(serializer.serialize(conversion), EventBatch.class); + EventBatch expected = gson.fromJson(generateConversionJson(), EventBatch.class); assertThat(actual, is(expected)); } @Test public void serializeConversionWithSessionId() throws Exception { - Conversion conversion = generateConversionWithSessionId(); + EventBatch conversion = generateConversionWithSessionId(); // can't compare JSON strings since orders could vary so compare objects instead - Conversion actual = gson.fromJson(serializer.serialize(conversion), Conversion.class); - Conversion expected = gson.fromJson(generateConversionWithSessionIdJson(), Conversion.class); + EventBatch actual = gson.fromJson(serializer.serialize(conversion), EventBatch.class); + EventBatch expected = gson.fromJson(generateConversionWithSessionIdJson(), EventBatch.class); assertThat(actual, is(expected)); } diff --git a/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JacksonSerializerTest.java b/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JacksonSerializerTest.java index 03792b9e2..76776aacd 100644 --- a/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JacksonSerializerTest.java +++ b/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JacksonSerializerTest.java @@ -18,8 +18,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; -import com.optimizely.ab.event.internal.payload.Conversion; -import com.optimizely.ab.event.internal.payload.Impression; +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.optimizely.ab.event.internal.payload.EventBatch; import org.junit.Test; @@ -40,44 +40,47 @@ public class JacksonSerializerTest { private JacksonSerializer serializer = new JacksonSerializer(); - private ObjectMapper mapper = new ObjectMapper(); + private ObjectMapper mapper = + new ObjectMapper().setPropertyNamingStrategy( + PropertyNamingStrategy.SNAKE_CASE); + @Test public void serializeImpression() throws IOException { - Impression impression = generateImpression(); + EventBatch impression = generateImpression(); // can't compare JSON strings since orders could vary so compare objects instead - Impression actual = mapper.readValue(serializer.serialize(impression), Impression.class); - Impression expected = mapper.readValue(generateImpressionJson(), Impression.class); + EventBatch actual = mapper.readValue(serializer.serialize(impression), EventBatch.class); + EventBatch expected = mapper.readValue(generateImpressionJson(), EventBatch.class); assertThat(actual, is(expected)); } @Test public void serializeImpressionWithSessionId() throws IOException { - Impression impression = generateImpressionWithSessionId(); + EventBatch impression = generateImpressionWithSessionId(); // can't compare JSON strings since orders could vary so compare objects instead - Impression actual = mapper.readValue(serializer.serialize(impression), Impression.class); - Impression expected = mapper.readValue(generateImpressionWithSessionIdJson(), Impression.class); + EventBatch actual = mapper.readValue(serializer.serialize(impression), EventBatch.class); + EventBatch expected = mapper.readValue(generateImpressionWithSessionIdJson(), EventBatch.class); assertThat(actual, is(expected)); } @Test public void serializeConversion() throws IOException { - Conversion conversion = generateConversion(); + EventBatch conversion = generateConversion(); // can't compare JSON strings since orders could vary so compare objects instead - Conversion actual = mapper.readValue(serializer.serialize(conversion), Conversion.class); - Conversion expected = mapper.readValue(generateConversionJson(), Conversion.class); + EventBatch actual = mapper.readValue(serializer.serialize(conversion), EventBatch.class); + EventBatch expected = mapper.readValue(generateConversionJson(), EventBatch.class); assertThat(actual, is(expected)); } @Test public void serializeConversionWithSessionId() throws IOException { - Conversion conversion = generateConversionWithSessionId(); + EventBatch conversion = generateConversionWithSessionId(); // can't compare JSON strings since orders could vary so compare objects instead - Conversion actual = mapper.readValue(serializer.serialize(conversion), Conversion.class); - Conversion expected = mapper.readValue(generateConversionWithSessionIdJson(), Conversion.class); + EventBatch actual = mapper.readValue(serializer.serialize(conversion), EventBatch.class); + EventBatch expected = mapper.readValue(generateConversionWithSessionIdJson(), EventBatch.class); assertThat(actual, is(expected)); } diff --git a/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JsonSerializerTest.java b/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JsonSerializerTest.java index f76d02f77..ff86538a5 100644 --- a/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JsonSerializerTest.java +++ b/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JsonSerializerTest.java @@ -16,8 +16,7 @@ */ package com.optimizely.ab.event.internal.serializer; -import com.optimizely.ab.event.internal.payload.Conversion; -import com.optimizely.ab.event.internal.payload.Impression; +import com.optimizely.ab.event.internal.payload.EventBatch; import org.json.JSONObject; @@ -42,7 +41,7 @@ public class JsonSerializerTest { @Test public void serializeImpression() throws IOException { - Impression impression = generateImpression(); + EventBatch impression = generateImpression(); // can't compare JSON strings since orders could vary so compare JSONObjects instead JSONObject actual = new JSONObject(serializer.serialize(impression)); JSONObject expected = new JSONObject(generateImpressionJson()); @@ -52,7 +51,7 @@ public void serializeImpression() throws IOException { @Test public void serializeImpressionWithSessionId() throws IOException { - Impression impression = generateImpressionWithSessionId(); + EventBatch impression = generateImpressionWithSessionId(); // can't compare JSON strings since orders could vary so compare JSONObjects instead JSONObject actual = new JSONObject(serializer.serialize(impression)); JSONObject expected = new JSONObject(generateImpressionWithSessionIdJson()); @@ -62,7 +61,7 @@ public void serializeImpressionWithSessionId() throws IOException { @Test public void serializeConversion() throws IOException { - Conversion conversion = generateConversion(); + EventBatch conversion = generateConversion(); // can't compare JSON strings since orders could vary so compare JSONObjects instead JSONObject actual = new JSONObject(serializer.serialize(conversion)); JSONObject expected = new JSONObject(generateConversionJson()); @@ -72,7 +71,7 @@ public void serializeConversion() throws IOException { @Test public void serializeConversionWithSessionId() throws IOException { - Conversion conversion = generateConversionWithSessionId(); + EventBatch conversion = generateConversionWithSessionId(); // can't compare JSON strings since orders could vary so compare JSONObjects instead JSONObject actual = new JSONObject(serializer.serialize(conversion)); JSONObject expected = new JSONObject(generateConversionWithSessionIdJson()); diff --git a/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JsonSimpleSerializerTest.java b/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JsonSimpleSerializerTest.java index da286b2ba..2c1fcdfa3 100644 --- a/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JsonSimpleSerializerTest.java +++ b/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JsonSimpleSerializerTest.java @@ -16,9 +16,7 @@ */ package com.optimizely.ab.event.internal.serializer; -import com.optimizely.ab.event.internal.payload.Conversion; -import com.optimizely.ab.event.internal.payload.Impression; - +import com.optimizely.ab.event.internal.payload.EventBatch; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; @@ -46,7 +44,7 @@ public class JsonSimpleSerializerTest { @Test public void serializeImpression() throws IOException, ParseException { - Impression impression = generateImpression(); + EventBatch impression = generateImpression(); // can't compare JSON strings since orders could vary so compare JSONObjects instead JSONObject actual = (JSONObject)parser.parse(serializer.serialize(impression)); JSONObject expected = (JSONObject)parser.parse(generateImpressionJson()); @@ -56,7 +54,7 @@ public void serializeImpression() throws IOException, ParseException { @Test public void serializeImpressionWithSessionId() throws IOException, ParseException { - Impression impression = generateImpressionWithSessionId(); + EventBatch impression = generateImpressionWithSessionId(); // can't compare JSON strings since orders could vary so compare JSONObjects instead JSONObject actual = (JSONObject)parser.parse(serializer.serialize(impression)); JSONObject expected = (JSONObject)parser.parse(generateImpressionWithSessionIdJson()); @@ -66,7 +64,7 @@ public void serializeImpressionWithSessionId() throws IOException, ParseExceptio @Test public void serializeConversion() throws IOException, ParseException { - Conversion conversion = generateConversion(); + EventBatch conversion = generateConversion(); // can't compare JSON strings since orders could vary so compare JSONObjects instead JSONObject actual = (JSONObject)parser.parse(serializer.serialize(conversion)); JSONObject expected = (JSONObject)parser.parse(generateConversionJson()); @@ -76,7 +74,7 @@ public void serializeConversion() throws IOException, ParseException { @Test public void serializeConversionWithSessionId() throws IOException, ParseException { - Conversion conversion = generateConversionWithSessionId(); + EventBatch conversion = generateConversionWithSessionId(); // can't compare JSON strings since orders could vary so compare JSONObjects instead JSONObject actual = (JSONObject)parser.parse(serializer.serialize(conversion)); JSONObject expected = (JSONObject)parser.parse(generateConversionWithSessionIdJson()); diff --git a/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/SerializerTestUtils.java b/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/SerializerTestUtils.java index e39464fc5..0e46737d8 100644 --- a/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/SerializerTestUtils.java +++ b/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/SerializerTestUtils.java @@ -18,14 +18,15 @@ import com.google.common.base.Charsets; import com.google.common.io.Resources; -import com.optimizely.ab.event.internal.payload.Conversion; +import com.optimizely.ab.event.internal.payload.Attribute; import com.optimizely.ab.event.internal.payload.Decision; -import com.optimizely.ab.event.internal.payload.EventMetric; -import com.optimizely.ab.event.internal.payload.Feature; -import com.optimizely.ab.event.internal.payload.Impression; -import com.optimizely.ab.event.internal.payload.LayerState; +import com.optimizely.ab.event.internal.payload.EventBatch; +import com.optimizely.ab.event.internal.payload.Event; +import com.optimizely.ab.event.internal.payload.Snapshot; +import com.optimizely.ab.event.internal.payload.Visitor; import java.io.IOException; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -42,75 +43,59 @@ public class SerializerTestUtils { private static final String experimentId = "5"; private static final String sessionId = "sessionid"; private static final String revision = "1"; - private static final Decision decision = new Decision(variationId, isLayerHoldback, experimentId); + private static final Decision decision = new Decision(layerId, experimentId, variationId, isLayerHoldback); private static final String featureId = "6"; private static final String featureName = "testfeature"; private static final String featureType = "custom"; private static final String featureValue = "testfeaturevalue"; private static final boolean shouldIndex = true; - private static final List userFeatures = Collections.singletonList( - new Feature(featureId, featureName, featureType, featureValue, shouldIndex)); + private static final List userFeatures = Collections.singletonList( + new Attribute(featureId, featureName, featureType, featureValue)); private static final boolean actionTriggered = true; - private static final List layerStates = - Collections.singletonList(new LayerState(layerId, revision, decision, actionTriggered)); private static final String eventEntityId = "7"; private static final String eventName = "testevent"; - private static final String eventMetricName = EventMetric.REVENUE_METRIC_TYPE; + private static final String eventMetricName = "revenue"; private static final long eventMetricValue = 5000L; - private static final List eventMetrics = Collections.singletonList( - new EventMetric(eventMetricName, eventMetricValue)); - private static final List eventFeatures = Collections.emptyList(); - - static Impression generateImpression() { - Impression impression = new Impression(); - impression.setVisitorId(visitorId); - impression.setTimestamp(timestamp); - impression.setIsGlobalHoldback(isGlobalHoldback); + + private static final List events = Collections.singletonList(new Event(timestamp, + "uuid", eventEntityId, eventName, null, 5000L, null, eventName, null)); + + static EventBatch generateImpression() { + Snapshot snapshot = new Snapshot(Arrays.asList(decision), events); + + Visitor vistor = new Visitor(visitorId, null, userFeatures, Arrays.asList(snapshot)); + EventBatch impression = new EventBatch(accountId, Arrays.asList(vistor), false, projectId,revision ); impression.setProjectId(projectId); - impression.setLayerId(layerId); impression.setAccountId(accountId); - impression.setDecision(decision); - impression.setUserFeatures(userFeatures); impression.setClientVersion("0.1.1"); - impression.setAnonymizeIP(true); + impression.setAnonymizeIp(true); impression.setRevision(revision); return impression; } - static Impression generateImpressionWithSessionId() { - Impression impression = generateImpression(); - impression.setSessionId(sessionId); + static EventBatch generateImpressionWithSessionId() { + EventBatch impression = generateImpression(); + impression.getVisitors().get(0).setSessionId(sessionId); return impression; } - static Conversion generateConversion() { - Conversion conversion = new Conversion(); - conversion.setVisitorId(visitorId); - conversion.setTimestamp(timestamp); - conversion.setProjectId(projectId); - conversion.setAccountId(accountId); - conversion.setUserFeatures(userFeatures); - conversion.setLayerStates(layerStates); - conversion.setEventEntityId(eventEntityId); - conversion.setEventName(eventName); - conversion.setEventMetrics(eventMetrics); - conversion.setEventFeatures(eventFeatures); - conversion.setIsGlobalHoldback(isGlobalHoldback); + static EventBatch generateConversion() { + EventBatch conversion = generateImpression(); conversion.setClientVersion("0.1.1"); - conversion.setAnonymizeIP(true); + conversion.setAnonymizeIp(true); conversion.setRevision(revision); return conversion; } - static Conversion generateConversionWithSessionId() { - Conversion conversion = generateConversion(); - conversion.setSessionId(sessionId); + static EventBatch generateConversionWithSessionId() { + EventBatch conversion = generateConversion(); + conversion.getVisitors().get(0).setSessionId(sessionId); return conversion; } diff --git a/core-api/src/test/resources/serializer/conversion-session-id.json b/core-api/src/test/resources/serializer/conversion-session-id.json index 8d3ae851f..2c1671da1 100644 --- a/core-api/src/test/resources/serializer/conversion-session-id.json +++ b/core-api/src/test/resources/serializer/conversion-session-id.json @@ -1,42 +1,44 @@ { - "visitorId": "testvisitor", - "timestamp": 12345, - "projectId": "1", - "accountId": "3", - "userFeatures": [ + "account_id": "3", + "visitors": [ { - "id": "6", - "name": "testfeature", - "type": "custom", - "value": "testfeaturevalue", - "shouldIndex": true + "snapshots": [ + { + "decisions": [ + { + "variation_id": "4", + "campaign_id": "2", + "experiment_id": "5", + "is_campaign_holdback": false + } + ], + "events": [ + { + "revenue": 5000, + "entity_id": "7", + "type": "testevent", + "uuid": "uuid", + "key": "testevent", + "timestamp": 12345 + } + ] + } + ], + "attributes": [ + { + "entity_id": "6", + "type": "custom", + "value": "testfeaturevalue", + "key": "testfeature" + } + ], + "visitor_id": "testvisitor", + "session_id": "sessionid" } ], - "layerStates": [ - { - "layerId": "2", - "revision": "1", - "decision": { - "variationId": "4", - "isLayerHoldback": false, - "experimentId": "5" - }, - "actionTriggered": true - } - ], - "eventEntityId": "7", - "eventName": "testevent", - "eventMetrics": [ - { - "name": "revenue", - "value": 5000 - } - ], - "eventFeatures": [], - "isGlobalHoldback": false, - "anonymizeIP": true, - "clientEngine": "java-sdk", - "clientVersion": "0.1.1", - "sessionId": "sessionid", + "client_name": "java-sdk", + "client_version": "0.1.1", + "anonymize_ip": true, + "project_id": "1", "revision": "1" } \ No newline at end of file diff --git a/core-api/src/test/resources/serializer/conversion.json b/core-api/src/test/resources/serializer/conversion.json index 5e432f3f4..2203295dd 100644 --- a/core-api/src/test/resources/serializer/conversion.json +++ b/core-api/src/test/resources/serializer/conversion.json @@ -1,41 +1,43 @@ { - "visitorId": "testvisitor", - "timestamp": 12345, - "projectId": "1", - "accountId": "3", - "userFeatures": [ + "account_id": "3", + "visitors": [ { - "id": "6", - "name": "testfeature", - "type": "custom", - "value": "testfeaturevalue", - "shouldIndex": true + "snapshots": [ + { + "decisions": [ + { + "variation_id": "4", + "campaign_id": "2", + "experiment_id": "5", + "is_campaign_holdback": false + } + ], + "events": [ + { + "revenue": 5000, + "entity_id": "7", + "type": "testevent", + "uuid": "uuid", + "key": "testevent", + "timestamp": 12345 + } + ] + } + ], + "attributes": [ + { + "entity_id": "6", + "type": "custom", + "value": "testfeaturevalue", + "key": "testfeature" + } + ], + "visitor_id": "testvisitor" } ], - "layerStates": [ - { - "layerId": "2", - "revision": "1", - "decision": { - "variationId": "4", - "isLayerHoldback": false, - "experimentId": "5" - }, - "actionTriggered": true - } - ], - "eventEntityId": "7", - "eventName": "testevent", - "eventMetrics": [ - { - "name": "revenue", - "value": 5000 - } - ], - "eventFeatures": [], - "isGlobalHoldback": false, - "anonymizeIP": true, - "clientEngine": "java-sdk", - "clientVersion": "0.1.1", + "client_name": "java-sdk", + "client_version": "0.1.1", + "anonymize_ip": true, + "project_id": "1", "revision": "1" } \ No newline at end of file diff --git a/core-api/src/test/resources/serializer/impression-session-id.json b/core-api/src/test/resources/serializer/impression-session-id.json index 476a29e27..2c1671da1 100644 --- a/core-api/src/test/resources/serializer/impression-session-id.json +++ b/core-api/src/test/resources/serializer/impression-session-id.json @@ -1,27 +1,44 @@ { - "visitorId": "testvisitor", - "timestamp": 12345, - "isGlobalHoldback": false, - "anonymizeIP": true, - "projectId": "1", - "decision": { - "variationId": "4", - "isLayerHoldback": false, - "experimentId": "5" - }, - "layerId": "2", - "accountId": "3", - "userFeatures": [ + "account_id": "3", + "visitors": [ { - "id": "6", - "name": "testfeature", - "type": "custom", - "value": "testfeaturevalue", - "shouldIndex": true + "snapshots": [ + { + "decisions": [ + { + "variation_id": "4", + "campaign_id": "2", + "experiment_id": "5", + "is_campaign_holdback": false + } + ], + "events": [ + { + "revenue": 5000, + "entity_id": "7", + "type": "testevent", + "uuid": "uuid", + "key": "testevent", + "timestamp": 12345 + } + ] + } + ], + "attributes": [ + { + "entity_id": "6", + "type": "custom", + "value": "testfeaturevalue", + "key": "testfeature" + } + ], + "visitor_id": "testvisitor", + "session_id": "sessionid" } ], - "clientEngine": "java-sdk", - "clientVersion": "0.1.1", - "sessionId": "sessionid", + "client_name": "java-sdk", + "client_version": "0.1.1", + "anonymize_ip": true, + "project_id": "1", "revision": "1" } \ No newline at end of file diff --git a/core-api/src/test/resources/serializer/impression.json b/core-api/src/test/resources/serializer/impression.json index 8d2f183d5..2203295dd 100644 --- a/core-api/src/test/resources/serializer/impression.json +++ b/core-api/src/test/resources/serializer/impression.json @@ -1,26 +1,43 @@ { - "visitorId": "testvisitor", - "timestamp": 12345, - "isGlobalHoldback": false, - "anonymizeIP": true, - "projectId": "1", - "decision": { - "variationId": "4", - "isLayerHoldback": false, - "experimentId": "5" - }, - "layerId": "2", - "accountId": "3", - "userFeatures": [ + "account_id": "3", + "visitors": [ { - "id": "6", - "name": "testfeature", - "type": "custom", - "value": "testfeaturevalue", - "shouldIndex": true + "snapshots": [ + { + "decisions": [ + { + "variation_id": "4", + "campaign_id": "2", + "experiment_id": "5", + "is_campaign_holdback": false + } + ], + "events": [ + { + "revenue": 5000, + "entity_id": "7", + "type": "testevent", + "uuid": "uuid", + "key": "testevent", + "timestamp": 12345 + } + ] + } + ], + "attributes": [ + { + "entity_id": "6", + "type": "custom", + "value": "testfeaturevalue", + "key": "testfeature" + } + ], + "visitor_id": "testvisitor" } ], - "clientEngine": "java-sdk", - "clientVersion": "0.1.1", + "client_name": "java-sdk", + "client_version": "0.1.1", + "anonymize_ip": true, + "project_id": "1", "revision": "1" } \ No newline at end of file