diff --git a/build.gradle b/build.gradle index 639091a3..8fea99c3 100644 --- a/build.gradle +++ b/build.gradle @@ -83,7 +83,6 @@ dependencies { testCompileOnly 'junit:junit:4.13' testRuntimeOnly 'org.junit.vintage:junit-vintage-engine' - testImplementation 'org.hamcrest:hamcrest:2.2' testImplementation 'com.squareup.okhttp3:mockwebserver:4.9.2' } diff --git a/src/main/java/com/snowplowanalytics/snowplow/tracker/Subject.java b/src/main/java/com/snowplowanalytics/snowplow/tracker/Subject.java index c61958e8..1b0ff8f3 100644 --- a/src/main/java/com/snowplowanalytics/snowplow/tracker/Subject.java +++ b/src/main/java/com/snowplowanalytics/snowplow/tracker/Subject.java @@ -21,6 +21,8 @@ /** * An object for managing extra event decoration. + * All the properties are optional. However, the timezone is set by default, + * to that of the server. */ public class Subject { diff --git a/src/main/java/com/snowplowanalytics/snowplow/tracker/Tracker.java b/src/main/java/com/snowplowanalytics/snowplow/tracker/Tracker.java index 8079ab9b..abf41b58 100644 --- a/src/main/java/com/snowplowanalytics/snowplow/tracker/Tracker.java +++ b/src/main/java/com/snowplowanalytics/snowplow/tracker/Tracker.java @@ -187,7 +187,7 @@ public DevicePlatform getPlatform() { * @return the wrapper containing the Tracker parameters */ public TrackerParameters getParameters() { - return this.parameters; + return parameters; } // --- Event Tracking Functions @@ -195,21 +195,41 @@ public TrackerParameters getParameters() { /** * Handles tracking the different types of events that * the Tracker can encounter. + * A TrackerPayload object - or more than one, in the case of eCommerceTransaction events - + * will be created from the Event. This is passed to the configured Emitter. + * If the event was successfully added to the Emitter buffer for sending, + * a list containing the payload's eventId string (a UUID) is returned. + * EcommerceTransactions will return all the relevant eventIds in the list. + * If the Emitter event buffer is full, the payload will be lost. In this case, this method + * returns a list containing null. + *

+ * Implementation note: As a side effect of adding a payload to the Emitter, + * it triggers an Emitter thread to emit a batch of events. * * @param event the event to track + * @return a list of eventIDs (UUIDs) */ - public void track(Event event) { + public List track(Event event) { + List results = new ArrayList<>(); // a list because Ecommerce events become multiple Payloads List processedEvents = eventTypeSpecificPreProcessing(event); for (Event processedEvent : processedEvents) { - // Event ID (eid) and device_created_timestamp (dtm) are generated when the Event is initialised + // Event ID (eid) and device_created_timestamp (dtm) are generated when + // the TrackerPayload is created TrackerPayload payload = (TrackerPayload) processedEvent.getPayload(); addTrackerParameters(payload); addContext(processedEvent, payload); addSubject(processedEvent, payload); - this.emitter.add(payload); + + boolean addedToBuffer = emitter.add(payload); + if (addedToBuffer) { + results.add(payload.getEventId()); + } else { + results.add(null); + } } + return results; } private List eventTypeSpecificPreProcessing(Event event) { @@ -223,7 +243,7 @@ private List eventTypeSpecificPreProcessing(Event event) { if (eventClass.equals(Unstructured.class)) { // Need to set the Base64 rule for Unstructured events final Unstructured unstructured = (Unstructured) event; - unstructured.setBase64Encode(this.parameters.getBase64Encoded()); + unstructured.setBase64Encode(parameters.getBase64Encoded()); eventList.add(unstructured); } else if (eventClass.equals(EcommerceTransaction.class)) { @@ -231,23 +251,19 @@ private List eventTypeSpecificPreProcessing(Event event) { eventList.add(ecommerceTransaction); // Track each item individually - for (final EcommerceTransactionItem item : ecommerceTransaction.getItems()) { - item.setDeviceCreatedTimestamp(ecommerceTransaction.getDeviceCreatedTimestamp()); - eventList.add(item); - } + eventList.addAll(ecommerceTransaction.getItems()); + } else if (eventClass.equals(Timing.class) || eventClass.equals(ScreenView.class)) { // Timing and ScreenView events are wrapper classes for Unstructured events // Need to create Unstructured events from them to send. final Unstructured unstructured = Unstructured.builder() .eventData((SelfDescribingJson) event.getPayload()) .customContext(event.getContext()) - .deviceCreatedTimestamp(event.getDeviceCreatedTimestamp()) .trueTimestamp(event.getTrueTimestamp()) - .eventId(event.getEventId()) .subject(event.getSubject()) .build(); - unstructured.setBase64Encode(this.parameters.getBase64Encoded()); + unstructured.setBase64Encode(parameters.getBase64Encoded()); eventList.add(unstructured); } else { @@ -257,10 +273,10 @@ private List eventTypeSpecificPreProcessing(Event event) { } private void addTrackerParameters(TrackerPayload payload) { - payload.add(Parameter.PLATFORM, this.parameters.getPlatform().toString()); - payload.add(Parameter.APP_ID, this.parameters.getAppId()); - payload.add(Parameter.NAMESPACE, this.parameters.getNamespace()); - payload.add(Parameter.TRACKER_VERSION, this.parameters.getTrackerVersion()); + payload.add(Parameter.PLATFORM, parameters.getPlatform().toString()); + payload.add(Parameter.APP_ID, parameters.getAppId()); + payload.add(Parameter.NAMESPACE, parameters.getNamespace()); + payload.add(Parameter.TRACKER_VERSION, parameters.getTrackerVersion()); } private void addContext(Event event, TrackerPayload payload) { @@ -269,7 +285,7 @@ private void addContext(Event event, TrackerPayload payload) { // Build the final context and add it to the payload if (entities != null && entities.size() > 0) { SelfDescribingJson envelope = getFinalContext(entities); - payload.addMap(envelope.getMap(), this.parameters.getBase64Encoded(), Parameter.CONTEXT_ENCODED, Parameter.CONTEXT); + payload.addMap(envelope.getMap(), parameters.getBase64Encoded(), Parameter.CONTEXT_ENCODED, Parameter.CONTEXT); } } @@ -293,8 +309,8 @@ private void addSubject(Event event, TrackerPayload payload) { // Add subject if available if (eventSubject != null) { payload.addMap(new HashMap<>(eventSubject.getSubject())); - } else if (this.subject != null) { - payload.addMap(new HashMap<>(this.subject.getSubject())); + } else if (subject != null) { + payload.addMap(new HashMap<>(subject.getSubject())); } } diff --git a/src/main/java/com/snowplowanalytics/snowplow/tracker/emitter/AbstractEmitter.java b/src/main/java/com/snowplowanalytics/snowplow/tracker/emitter/AbstractEmitter.java index e56e4c14..00779196 100644 --- a/src/main/java/com/snowplowanalytics/snowplow/tracker/emitter/AbstractEmitter.java +++ b/src/main/java/com/snowplowanalytics/snowplow/tracker/emitter/AbstractEmitter.java @@ -133,7 +133,7 @@ protected AbstractEmitter(final Builder builder) { * @param payload an payload */ @Override - public abstract void add(TrackerPayload payload); + public abstract boolean add(TrackerPayload payload); /** * Customize the emitter batch size to any valid integer greater than zero. diff --git a/src/main/java/com/snowplowanalytics/snowplow/tracker/emitter/BatchEmitter.java b/src/main/java/com/snowplowanalytics/snowplow/tracker/emitter/BatchEmitter.java index c76951eb..c13ea4fe 100644 --- a/src/main/java/com/snowplowanalytics/snowplow/tracker/emitter/BatchEmitter.java +++ b/src/main/java/com/snowplowanalytics/snowplow/tracker/emitter/BatchEmitter.java @@ -108,13 +108,12 @@ protected BatchEmitter(final Builder builder) { /** * Adds a TrackerPayload to the concurrent queue buffer *

- * Implementation note: Be aware that calling `close()` on a BatchEmitter instance - * has a side-effect and will shutdown that ExecutorService. + * Implementation note: As a side effect it triggers an Emitter thread to emit a batch of events. * * @param payload a payload */ @Override - public void add(final TrackerPayload payload) { + public boolean add(final TrackerPayload payload) { boolean result = eventStore.addEvent(payload); if (!isClosing) { @@ -126,6 +125,8 @@ public void add(final TrackerPayload payload) { if (!result) { LOGGER.error("Unable to add payload to emitter, emitter buffer is full"); } + + return result; } /** diff --git a/src/main/java/com/snowplowanalytics/snowplow/tracker/emitter/Emitter.java b/src/main/java/com/snowplowanalytics/snowplow/tracker/emitter/Emitter.java index aac70315..c9cb9b50 100644 --- a/src/main/java/com/snowplowanalytics/snowplow/tracker/emitter/Emitter.java +++ b/src/main/java/com/snowplowanalytics/snowplow/tracker/emitter/Emitter.java @@ -27,7 +27,7 @@ public interface Emitter { * * @param payload a payload to be emitted */ - void add(TrackerPayload payload); + boolean add(TrackerPayload payload); /** * Customize the emitter batch size to any valid integer diff --git a/src/main/java/com/snowplowanalytics/snowplow/tracker/emitter/SimpleEmitter.java b/src/main/java/com/snowplowanalytics/snowplow/tracker/emitter/SimpleEmitter.java index 715da53b..a6cd3bf8 100644 --- a/src/main/java/com/snowplowanalytics/snowplow/tracker/emitter/SimpleEmitter.java +++ b/src/main/java/com/snowplowanalytics/snowplow/tracker/emitter/SimpleEmitter.java @@ -56,8 +56,12 @@ protected SimpleEmitter(final Builder builder) { * @param payload a payload */ @Override - public void add(TrackerPayload payload) { + public boolean add(TrackerPayload payload) { executor.execute(getGetRequestRunnable(payload)); + + // This result doesn't mean anything + // The return type is for BatchEmitter's benefit + return true; } /** diff --git a/src/main/java/com/snowplowanalytics/snowplow/tracker/events/AbstractEvent.java b/src/main/java/com/snowplowanalytics/snowplow/tracker/events/AbstractEvent.java index 92bb8988..369bb3a1 100644 --- a/src/main/java/com/snowplowanalytics/snowplow/tracker/events/AbstractEvent.java +++ b/src/main/java/com/snowplowanalytics/snowplow/tracker/events/AbstractEvent.java @@ -40,22 +40,16 @@ public abstract class AbstractEvent implements Event { protected final List context; - protected long deviceCreatedTimestamp; - /** * The true timestamp may be null if none is set. */ protected Long trueTimestamp; - - protected final String eventId; protected final Subject subject; public static abstract class Builder> { private List context = new LinkedList<>(); - private long deviceCreatedTimestamp = System.currentTimeMillis(); protected Long trueTimestamp = null; - private String eventId = Utils.getEventId(); private Subject subject = null; protected abstract T self(); @@ -71,31 +65,6 @@ public T customContext(List context) { return self(); } - /** - * A custom event timestamp. - * - * @param timestamp the event timestamp as - * unix epoch - * @return itself - * Use {@link #trueTimestamp} or {@link #deviceCreatedTimestamp} - */ - @Deprecated - public T timestamp(long timestamp) { - return deviceCreatedTimestamp(timestamp); - } - - /** - * Adjust the device-created timestamp. This is usually not what you want, check {@link #trueTimestamp}. - * - * @param timestamp the event timestamp as - * unix epoch - * @return itself - */ - public T deviceCreatedTimestamp(long timestamp) { - this.deviceCreatedTimestamp = timestamp; - return self(); - } - /** * The true timestamp of that event (as determined by the user). * @@ -108,17 +77,6 @@ public T trueTimestamp(Long timestamp) { return self(); } - /** - * A custom eventId for the event. - * - * @param eventId the eventId - * @return itself - */ - public T eventId(String eventId) { - this.eventId = eventId; - return self(); - } - /** * A custom subject for the event. * @@ -146,13 +104,9 @@ protected AbstractEvent(Builder builder) { // Precondition checks Preconditions.checkNotNull(builder.context); - Preconditions.checkNotNull(builder.eventId); - Preconditions.checkArgument(!builder.eventId.isEmpty(), "eventId cannot be empty"); this.context = builder.context; - this.deviceCreatedTimestamp = builder.deviceCreatedTimestamp; this.trueTimestamp = builder.trueTimestamp; - this.eventId = builder.eventId; this.subject = builder.subject; } @@ -164,23 +118,6 @@ public List getContext() { return new ArrayList<>(this.context); } - /** - * @return the event's timestamp - * @deprecated Use {@link #getTrueTimestamp()} or {@link #getDeviceCreatedTimestamp()} - */ - @Override - public long getTimestamp() { - return this.deviceCreatedTimestamp; - } - - /** - * @return the event's device created timestamp. - */ - @Override - public long getDeviceCreatedTimestamp() { - return deviceCreatedTimestamp; - } - /** * @return the event's true timestamp. */ @@ -189,14 +126,6 @@ public Long getTrueTimestamp() { return trueTimestamp; } - /** - * @return the event id - */ - @Override - public String getEventId() { - return this.eventId; - } - /** * @return the event subject */ @@ -217,12 +146,10 @@ public Subject getSubject() { * @param payload the payload to add to. * @return the TrackerPayload with appended values. */ - protected TrackerPayload putDefaultParams(TrackerPayload payload) { - payload.add(Parameter.EID, getEventId()); - if (getTrueTimestamp()!=null) { + protected TrackerPayload putTrueTimestamp(TrackerPayload payload) { + if (getTrueTimestamp() != null) { payload.add(Parameter.TRUE_TIMESTAMP, Long.toString(getTrueTimestamp())); } - payload.add(Parameter.DEVICE_CREATED_TIMESTAMP, Long.toString(getDeviceCreatedTimestamp())); return payload; } } diff --git a/src/main/java/com/snowplowanalytics/snowplow/tracker/events/EcommerceTransaction.java b/src/main/java/com/snowplowanalytics/snowplow/tracker/events/EcommerceTransaction.java index d7f31752..9151e014 100644 --- a/src/main/java/com/snowplowanalytics/snowplow/tracker/events/EcommerceTransaction.java +++ b/src/main/java/com/snowplowanalytics/snowplow/tracker/events/EcommerceTransaction.java @@ -209,7 +209,7 @@ public TrackerPayload getPayload() { payload.add(Parameter.TR_STATE, this.state); payload.add(Parameter.TR_COUNTRY, this.country); payload.add(Parameter.TR_CURRENCY, this.currency); - return putDefaultParams(payload); + return putTrueTimestamp(payload); } /** diff --git a/src/main/java/com/snowplowanalytics/snowplow/tracker/events/EcommerceTransactionItem.java b/src/main/java/com/snowplowanalytics/snowplow/tracker/events/EcommerceTransactionItem.java index 0a928f4c..37a89cbc 100644 --- a/src/main/java/com/snowplowanalytics/snowplow/tracker/events/EcommerceTransactionItem.java +++ b/src/main/java/com/snowplowanalytics/snowplow/tracker/events/EcommerceTransactionItem.java @@ -140,15 +140,6 @@ protected EcommerceTransactionItem(Builder builder) { this.currency = builder.currency; } - /** - * @param timestamp the new timestamp - * Use {@link #setTrueTimestamp(long)} or {@link #setTrueTimestamp(long)} - */ - @Deprecated - public void setTimestamp(long timestamp) { - setDeviceCreatedTimestamp(timestamp); - } - /** * @param timestamp the new timestamp */ @@ -156,13 +147,6 @@ public void setTrueTimestamp(long timestamp) { this.trueTimestamp = timestamp; } - /** - * @param timestamp the new timestamp - */ - public void setDeviceCreatedTimestamp(Long timestamp) { - this.deviceCreatedTimestamp = timestamp; - } - /** * Returns a TrackerPayload which can be stored into * the local database. @@ -179,6 +163,6 @@ public TrackerPayload getPayload() { payload.add(Parameter.TI_ITEM_PRICE, Double.toString(this.price)); payload.add(Parameter.TI_ITEM_QUANTITY, Integer.toString(this.quantity)); payload.add(Parameter.TI_ITEM_CURRENCY, this.currency); - return putDefaultParams(payload); + return putTrueTimestamp(payload); } } diff --git a/src/main/java/com/snowplowanalytics/snowplow/tracker/events/Event.java b/src/main/java/com/snowplowanalytics/snowplow/tracker/events/Event.java index 935279a1..b9909913 100644 --- a/src/main/java/com/snowplowanalytics/snowplow/tracker/events/Event.java +++ b/src/main/java/com/snowplowanalytics/snowplow/tracker/events/Event.java @@ -28,28 +28,11 @@ public interface Event { */ List getContext(); - /** - * @return the event's timestamp - * Use {@link #getTrueTimestamp()} or {@link #getDeviceCreatedTimestamp()} - */ - @Deprecated - long getTimestamp(); - /** * @return the event's true timestamp */ Long getTrueTimestamp(); - /** - * @return the event's device created timestamp - */ - long getDeviceCreatedTimestamp(); - - /** - * @return the event id - */ - String getEventId(); - /** * @return the event subject */ diff --git a/src/main/java/com/snowplowanalytics/snowplow/tracker/events/PageView.java b/src/main/java/com/snowplowanalytics/snowplow/tracker/events/PageView.java index 27bed72f..87da01f4 100644 --- a/src/main/java/com/snowplowanalytics/snowplow/tracker/events/PageView.java +++ b/src/main/java/com/snowplowanalytics/snowplow/tracker/events/PageView.java @@ -102,6 +102,6 @@ public TrackerPayload getPayload() { payload.add(Parameter.PAGE_URL, this.pageUrl); payload.add(Parameter.PAGE_TITLE, this.pageTitle); payload.add(Parameter.PAGE_REFR, this.referrer); - return putDefaultParams(payload); + return putTrueTimestamp(payload); } } diff --git a/src/main/java/com/snowplowanalytics/snowplow/tracker/events/ScreenView.java b/src/main/java/com/snowplowanalytics/snowplow/tracker/events/ScreenView.java index d84adc57..9b1bf1a0 100644 --- a/src/main/java/com/snowplowanalytics/snowplow/tracker/events/ScreenView.java +++ b/src/main/java/com/snowplowanalytics/snowplow/tracker/events/ScreenView.java @@ -19,6 +19,8 @@ import com.snowplowanalytics.snowplow.tracker.payload.SelfDescribingJson; import com.snowplowanalytics.snowplow.tracker.payload.TrackerPayload; +import java.util.LinkedHashMap; + public class ScreenView extends AbstractEvent { private final String name; @@ -79,9 +81,9 @@ protected ScreenView(Builder builder) { * @return the payload as a SelfDescribingJson. */ public SelfDescribingJson getPayload() { - TrackerPayload payload = new TrackerPayload(); - payload.add(Parameter.SV_ID, this.id); - payload.add(Parameter.SV_NAME, this.name); + LinkedHashMap payload = new LinkedHashMap<>(); + payload.put(Parameter.SV_ID, this.id); + payload.put(Parameter.SV_NAME, this.name); return new SelfDescribingJson(Constants.SCHEMA_SCREEN_VIEW, payload); } } diff --git a/src/main/java/com/snowplowanalytics/snowplow/tracker/events/Structured.java b/src/main/java/com/snowplowanalytics/snowplow/tracker/events/Structured.java index e80da843..06e7f282 100644 --- a/src/main/java/com/snowplowanalytics/snowplow/tracker/events/Structured.java +++ b/src/main/java/com/snowplowanalytics/snowplow/tracker/events/Structured.java @@ -131,6 +131,6 @@ public TrackerPayload getPayload() { payload.add(Parameter.SE_PROPERTY, this.property); payload.add(Parameter.SE_VALUE, this.value != null ? Double.toString(this.value) : null); - return putDefaultParams(payload); + return putTrueTimestamp(payload); } } diff --git a/src/main/java/com/snowplowanalytics/snowplow/tracker/events/Unstructured.java b/src/main/java/com/snowplowanalytics/snowplow/tracker/events/Unstructured.java index a73b4575..37174422 100644 --- a/src/main/java/com/snowplowanalytics/snowplow/tracker/events/Unstructured.java +++ b/src/main/java/com/snowplowanalytics/snowplow/tracker/events/Unstructured.java @@ -89,6 +89,6 @@ public TrackerPayload getPayload() { payload.add(Parameter.EVENT, Constants.EVENT_UNSTRUCTURED); payload.addMap(envelope.getMap(), this.base64Encode, Parameter.UNSTRUCTURED_ENCODED, Parameter.UNSTRUCTURED); - return putDefaultParams(payload); + return putTrueTimestamp(payload); } } diff --git a/src/main/java/com/snowplowanalytics/snowplow/tracker/payload/TrackerPayload.java b/src/main/java/com/snowplowanalytics/snowplow/tracker/payload/TrackerPayload.java index 93bb9303..21af7f98 100644 --- a/src/main/java/com/snowplowanalytics/snowplow/tracker/payload/TrackerPayload.java +++ b/src/main/java/com/snowplowanalytics/snowplow/tracker/payload/TrackerPayload.java @@ -16,6 +16,7 @@ import java.util.LinkedHashMap; import java.util.Map; +import com.snowplowanalytics.snowplow.tracker.constants.Parameter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,6 +30,25 @@ public class TrackerPayload implements Payload { private static final Logger LOGGER = LoggerFactory.getLogger(TrackerPayload.class); protected final Map payload = new LinkedHashMap<>(); + private final String eventId; + private final Long deviceCreatedTimestamp; + + + public TrackerPayload() { + eventId = Utils.getEventId(); + deviceCreatedTimestamp = System.currentTimeMillis(); + + add(Parameter.EID, eventId); + add(Parameter.DEVICE_CREATED_TIMESTAMP, Long.toString(deviceCreatedTimestamp)); + } + + public String getEventId() { + return eventId; + } + + public Long getDeviceCreatedTimestamp() { + return deviceCreatedTimestamp; + } /** * Add a key-value pair to the payload: - Checks that the key is not null or diff --git a/src/test/java/com/snowplowanalytics/snowplow/tracker/TrackerTest.java b/src/test/java/com/snowplowanalytics/snowplow/tracker/TrackerTest.java index b2840266..8b922cc0 100644 --- a/src/test/java/com/snowplowanalytics/snowplow/tracker/TrackerTest.java +++ b/src/test/java/com/snowplowanalytics/snowplow/tracker/TrackerTest.java @@ -15,8 +15,6 @@ import java.util.*; import static java.util.Collections.singletonList; -import com.snowplowanalytics.snowplow.tracker.emitter.BatchPayload; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; @@ -31,31 +29,23 @@ public class TrackerTest { public static final String EXPECTED_CONTEXTS = "{\"schema\":\"iglu:com.snowplowanalytics.snowplow/contexts/jsonschema/1-0-1\",\"data\":[{\"schema\":\"schema\",\"data\":{\"foo\":\"bar\"}}]}"; - public static final String EXPECTED_EVENT_ID = "15e9b149-6029-4f6e-8447-5b9797c9e6be"; public static class MockEmitter implements Emitter { public ArrayList eventList = new ArrayList<>(); @Override - public void add(TrackerPayload payload) { + public boolean add(TrackerPayload payload) { eventList.add(payload); + return true; } - @Override public void setBatchSize(int batchSize) {} - @Override public void flushBuffer() {} - @Override - public int getBatchSize() { - return 0; - } - + public int getBatchSize() { return 0; } @Override - public List getBuffer() { - return null; - } + public List getBuffer() { return null; } } MockEmitter mockEmitter; @@ -75,6 +65,57 @@ public void setUp() { // --- Event Tests + @Test + public void testTrackReturnsEventIdIfSuccessful() throws InterruptedException { + // a list to allow for eCommerceTransaction + List result = tracker.track(Unstructured.builder() + .eventData(new SelfDescribingJson( + "iglu:com.snowplowanalytics.snowplow/example/jsonschema/1-0-0", + ImmutableMap.of("foo", "bar") + )) + .build()); + + Thread.sleep(500); + + boolean isValidEventId = true; + try { + UUID.fromString(result.get(0)); + } catch (Exception e) { + isValidEventId = false; + } + + assertTrue(isValidEventId); + } + + @Test + public void testTrackReturnsNullIfEventWasDropped() throws InterruptedException { + class FailingMockEmitter implements Emitter { + @Override + public boolean add(TrackerPayload payload) { return false; } + @Override + public void setBatchSize(int batchSize) {} + @Override + public void flushBuffer() {} + @Override + public int getBatchSize() { return 0; } + @Override + public List getBuffer() { return null; } + } + FailingMockEmitter failingMockEmitter = new FailingMockEmitter(); + tracker = new Tracker.TrackerBuilder(failingMockEmitter, "AF003", "cloudfront").build(); + + List result = tracker.track(Unstructured.builder() + .eventData(new SelfDescribingJson( + "iglu:com.snowplowanalytics.snowplow/example/jsonschema/1-0-0", + ImmutableMap.of("foo", "bar") + )) + .build()); + + Thread.sleep(500); + + assertNull(result.get(0)); + } + @Test public void testEcommerceEvent() throws InterruptedException { // Given @@ -87,9 +128,7 @@ public void testEcommerceEvent() throws InterruptedException { .category("category") .currency("currency") .customContext(contexts) - .deviceCreatedTimestamp(123456) .trueTimestamp(456789L) - .eventId(EXPECTED_EVENT_ID) .build(); // When @@ -105,9 +144,7 @@ public void testEcommerceEvent() throws InterruptedException { .currency("currency") .items(item) .customContext(contexts) - .deviceCreatedTimestamp(123456) .trueTimestamp(456789L) - .eventId(EXPECTED_EVENT_ID) .build()); // Then @@ -117,15 +154,13 @@ public void testEcommerceEvent() throws InterruptedException { assertEquals(2, results.size()); Map result1 = results.get(0).getMap(); - assertEquals(ImmutableMap.builder() + Map expected1 = ImmutableMap.builder() .put("e", "tr") .put("tr_cu", "currency") .put("co", EXPECTED_CONTEXTS) - .put("eid", EXPECTED_EVENT_ID) .put("tna", "AF003") .put("aid", "cloudfront") .put("tr_sh", "3.0") - .put("dtm", "123456") .put("ttm", "456789") .put("tz", "Etc/UTC") .put("tr_co", "country") @@ -137,19 +172,19 @@ public void testEcommerceEvent() throws InterruptedException { .put("tr_tt", "1.0") .put("tr_ci", "city") .put("tr_st", "state") - .build(), result1); + .build(); + + assertTrue(result1.entrySet().containsAll(expected1.entrySet())); Map result2 = results.get(1).getMap(); - assertEquals(ImmutableMap.builder() + Map expected2 = ImmutableMap.builder() .put("ti_nm", "name") .put("ti_id", "order_id") .put("e", "ti") .put("co", EXPECTED_CONTEXTS) - .put("eid", EXPECTED_EVENT_ID) .put("tna", "AF003") .put("aid", "cloudfront") .put("ti_cu", "currency") - .put("dtm", "123456") .put("ttm", "456789") .put("tz", "Etc/UTC") .put("ti_pr", "1.0") @@ -158,7 +193,9 @@ public void testEcommerceEvent() throws InterruptedException { .put("tv", Version.TRACKER) .put("ti_ca", "category") .put("ti_sk", "sku") - .build(), result2); + .build(); + + assertTrue(result2.entrySet().containsAll(expected2.entrySet())); } @Test @@ -166,32 +203,30 @@ public void testUnstructuredEventWithContext() throws InterruptedException { // When tracker.track(Unstructured.builder() .eventData(new SelfDescribingJson( - "payload", + "iglu:com.snowplowanalytics.snowplow/example/jsonschema/1-0-0", ImmutableMap.of("foo", "bar") )) .customContext(contexts) - .deviceCreatedTimestamp(123456) .trueTimestamp(456789L) - .eventId(EXPECTED_EVENT_ID) .build()); // Then Thread.sleep(500); Map result = mockEmitter.eventList.get(0).getMap(); - assertEquals(ImmutableMap.builder() + Map expected = ImmutableMap.builder() .put("p", "srv") .put("tv", Version.TRACKER) .put("e", "ue") .put("co", EXPECTED_CONTEXTS) - .put("eid", EXPECTED_EVENT_ID) .put("tna", "AF003") .put("tz", "Etc/UTC") - .put("ue_pr", "{\"schema\":\"iglu:com.snowplowanalytics.snowplow/unstruct_event/jsonschema/1-0-0\",\"data\":{\"schema\":\"payload\",\"data\":{\"foo\":\"bar\"}}}") - .put("dtm", "123456") + .put("ue_pr", "{\"schema\":\"iglu:com.snowplowanalytics.snowplow/unstruct_event/jsonschema/1-0-0\",\"data\":{\"schema\":\"iglu:com.snowplowanalytics.snowplow/example/jsonschema/1-0-0\",\"data\":{\"foo\":\"bar\"}}}") .put("ttm", "456789") .put("aid", "cloudfront") - .build(), result); + .build(); + + assertTrue(result.entrySet().containsAll(expected.entrySet())); } @Test @@ -199,30 +234,28 @@ public void testUnstructuredEventWithoutContext() throws InterruptedException { // When tracker.track(Unstructured.builder() .eventData(new SelfDescribingJson( - "payload", + "iglu:com.snowplowanalytics.snowplow/example/jsonschema/1-0-0", ImmutableMap.of("foo", "baær") )) - .deviceCreatedTimestamp(123456) .trueTimestamp(456789L) - .eventId(EXPECTED_EVENT_ID) .build()); // Then Thread.sleep(500); Map result = mockEmitter.eventList.get(0).getMap(); - assertEquals(ImmutableMap.builder() + Map expected = ImmutableMap.builder() .put("p", "srv") .put("tv", Version.TRACKER) - .put("eid", EXPECTED_EVENT_ID) .put("e", "ue") .put("tna", "AF003") .put("tz", "Etc/UTC") - .put("ue_pr", "{\"schema\":\"iglu:com.snowplowanalytics.snowplow/unstruct_event/jsonschema/1-0-0\",\"data\":{\"schema\":\"payload\",\"data\":{\"foo\":\"baær\"}}}") - .put("dtm", "123456") + .put("ue_pr", "{\"schema\":\"iglu:com.snowplowanalytics.snowplow/unstruct_event/jsonschema/1-0-0\",\"data\":{\"schema\":\"iglu:com.snowplowanalytics.snowplow/example/jsonschema/1-0-0\",\"data\":{\"foo\":\"baær\"}}}") .put("ttm", "456789") .put("aid", "cloudfront") - .build(), result); + .build(); + + assertTrue(result.entrySet().containsAll(expected.entrySet())); } @Test @@ -230,33 +263,31 @@ public void testUnstructuredEventWithoutTrueTimestamp() throws InterruptedExcept // When tracker.track(Unstructured.builder() .eventData(new SelfDescribingJson( - "payload", - ImmutableMap.of("foo", "baær") + "iglu:com.snowplowanalytics.snowplow/example/jsonschema/1-0-0", + ImmutableMap.of("foo", "bar") )) - .deviceCreatedTimestamp(123456) - .eventId(EXPECTED_EVENT_ID) .build()); // Then Thread.sleep(500); Map result = mockEmitter.eventList.get(0).getMap(); - assertEquals(ImmutableMap.builder() + Map expected = ImmutableMap.builder() .put("p", "srv") .put("tv", Version.TRACKER) - .put("eid", EXPECTED_EVENT_ID) .put("e", "ue") .put("tna", "AF003") .put("tz", "Etc/UTC") - .put("ue_pr", "{\"schema\":\"iglu:com.snowplowanalytics.snowplow/unstruct_event/jsonschema/1-0-0\",\"data\":{\"schema\":\"payload\",\"data\":{\"foo\":\"baær\"}}}") - .put("dtm", "123456") + .put("ue_pr", "{\"schema\":\"iglu:com.snowplowanalytics.snowplow/unstruct_event/jsonschema/1-0-0\",\"data\":{\"schema\":\"iglu:com.snowplowanalytics.snowplow/example/jsonschema/1-0-0\",\"data\":{\"foo\":\"baær\"}}}") .put("aid", "cloudfront") - .build(), result); + .build(); + + assertTrue(result.entrySet().containsAll(expected.entrySet())); } @Test public void testTrackPageView() throws InterruptedException { - tracker = new Tracker.TrackerBuilder(this.mockEmitter, "AF003", "cloudfront") + tracker = new Tracker.TrackerBuilder(mockEmitter, "AF003", "cloudfront") .subject(new Subject.SubjectBuilder().build()) .base64(false) .build(); @@ -268,17 +299,14 @@ public void testTrackPageView() throws InterruptedException { .pageTitle("title") .referrer("referer") .customContext(contexts) - .deviceCreatedTimestamp(123456) .trueTimestamp(456789L) - .eventId(EXPECTED_EVENT_ID) .build()); // Then Thread.sleep(500); Map result = mockEmitter.eventList.get(0).getMap(); - assertEquals(ImmutableMap.builder() - .put("dtm", "123456") + Map expected = ImmutableMap.builder() .put("ttm", "456789") .put("tz", "Etc/UTC") .put("e", "pv") @@ -286,12 +314,13 @@ public void testTrackPageView() throws InterruptedException { .put("tv", Version.TRACKER) .put("p", "srv") .put("co", EXPECTED_CONTEXTS) - .put("eid", EXPECTED_EVENT_ID) .put("tna", "AF003") .put("aid", "cloudfront") .put("refr", "referer") .put("url", "url") - .build(), result); + .build(); + + assertTrue(result.entrySet().containsAll(expected.entrySet())); } @Test @@ -301,20 +330,15 @@ public void testTrackTwoEvents() throws InterruptedException { .pageUrl("url") .pageTitle("title") .referrer("referer") - .deviceCreatedTimestamp(123456) - .trueTimestamp(456789L) - .eventId("9783090a-dace-4c85-a75c-933b4596a6c5") + .trueTimestamp(123456L) .build()); - Thread.sleep(500); - - tracker.track(PageView.builder() - .pageUrl("url") - .pageTitle("title") - .referrer("referer") - .deviceCreatedTimestamp(123456) + tracker.track(Unstructured.builder() + .eventData(new SelfDescribingJson( + "iglu:com.snowplowanalytics.snowplow/example/jsonschema/1-0-0", + ImmutableMap.of("foo", "bar") + )) .trueTimestamp(456789L) - .eventId("39139d43-ea13-4163-8559-adea258bf9c4") .build()); // Then @@ -324,36 +348,34 @@ public void testTrackTwoEvents() throws InterruptedException { assertEquals(2, results.size()); Map result1 = results.get(0).getMap(); - assertEquals(ImmutableMap.builder() - .put("dtm", "123456") - .put("ttm", "456789") + Map expected1 = ImmutableMap.builder() + .put("ttm", "123456") .put("tz", "Etc/UTC") .put("e", "pv") .put("page", "title") .put("tv", Version.TRACKER) .put("p", "srv") - .put("eid", "9783090a-dace-4c85-a75c-933b4596a6c5") .put("tna", "AF003") .put("aid", "cloudfront") .put("refr", "referer") .put("url", "url") - .build(), result1); + .build(); + + assertTrue(result1.entrySet().containsAll(expected1.entrySet())); Map result2 = results.get(1).getMap(); - assertEquals(ImmutableMap.builder() - .put("dtm", "123456") + Map expected2 = ImmutableMap.builder() .put("ttm", "456789") - .put("tz", "Etc/UTC") - .put("e", "pv") - .put("page", "title") - .put("tv", Version.TRACKER) .put("p", "srv") - .put("eid", "39139d43-ea13-4163-8559-adea258bf9c4") + .put("tv", Version.TRACKER) + .put("e", "ue") .put("tna", "AF003") + .put("tz", "Etc/UTC") + .put("ue_pr", "{\"schema\":\"iglu:com.snowplowanalytics.snowplow/unstruct_event/jsonschema/1-0-0\",\"data\":{\"schema\":\"iglu:com.snowplowanalytics.snowplow/example/jsonschema/1-0-0\",\"data\":{\"foo\":\"bar\"}}}") .put("aid", "cloudfront") - .put("refr", "referer") - .put("url", "url") - .build(), result2); + .build(); + + assertTrue(result2.entrySet().containsAll(expected2.entrySet())); } @Test @@ -363,28 +385,26 @@ public void testTrackScreenView() throws InterruptedException { .name("name") .id("id") .customContext(contexts) - .deviceCreatedTimestamp(123456) .trueTimestamp(456789L) - .eventId(EXPECTED_EVENT_ID) .build()); // Then Thread.sleep(500); Map result = mockEmitter.eventList.get(0).getMap(); - assertEquals(ImmutableMap.builder() - .put("dtm", "123456") + Map expected = ImmutableMap.builder() .put("ttm", "456789") .put("tz", "Etc/UTC") .put("e", "ue") .put("tv", Version.TRACKER) .put("p", "srv") .put("co", EXPECTED_CONTEXTS) - .put("eid", EXPECTED_EVENT_ID) .put("tna", "AF003") .put("aid", "cloudfront") .put("ue_pr", "{\"schema\":\"iglu:com.snowplowanalytics.snowplow/unstruct_event/jsonschema/1-0-0\",\"data\":{\"schema\":\"iglu:com.snowplowanalytics.snowplow/screen_view/jsonschema/1-0-0\",\"data\":{\"id\":\"id\",\"name\":\"name\"}}}") - .build(), result); + .build(); + + assertTrue(result.entrySet().containsAll(expected.entrySet())); } @Test @@ -393,27 +413,25 @@ public void testTrackScreenViewWithTimestamp() throws InterruptedException { tracker.track(ScreenView.builder() .name("name") .id("id") - .deviceCreatedTimestamp(123456) .trueTimestamp(456789L) - .eventId(EXPECTED_EVENT_ID) .build()); // Then Thread.sleep(500); Map result = mockEmitter.eventList.get(0).getMap(); - assertEquals(ImmutableMap.builder() - .put("dtm", "123456") + Map expected = ImmutableMap.builder() .put("ttm", "456789") .put("tz", "Etc/UTC") .put("e", "ue") .put("tv", Version.TRACKER) .put("p", "srv") - .put("eid", EXPECTED_EVENT_ID) .put("tna", "AF003") .put("aid", "cloudfront") .put("ue_pr", "{\"schema\":\"iglu:com.snowplowanalytics.snowplow/unstruct_event/jsonschema/1-0-0\",\"data\":{\"schema\":\"iglu:com.snowplowanalytics.snowplow/screen_view/jsonschema/1-0-0\",\"data\":{\"id\":\"id\",\"name\":\"name\"}}}") - .build(), result); + .build(); + + assertTrue(result.entrySet().containsAll(expected.entrySet())); } @Test @@ -423,28 +441,26 @@ public void testTrackScreenViewWithDefaultContextAndTimestamp() throws Interrupt .name("name") .id("id") .customContext(contexts) - .deviceCreatedTimestamp(123456) .trueTimestamp(456789L) - .eventId(EXPECTED_EVENT_ID) .build()); // Then Thread.sleep(500); Map result = mockEmitter.eventList.get(0).getMap(); - assertEquals(ImmutableMap.builder() + Map expected = ImmutableMap.builder() .put("p", "srv") .put("tv", Version.TRACKER) .put("e", "ue") .put("co", EXPECTED_CONTEXTS) - .put("eid", EXPECTED_EVENT_ID) .put("tna", "AF003") .put("tz", "Etc/UTC") .put("ue_pr", "{\"schema\":\"iglu:com.snowplowanalytics.snowplow/unstruct_event/jsonschema/1-0-0\",\"data\":{\"schema\":\"iglu:com.snowplowanalytics.snowplow/screen_view/jsonschema/1-0-0\",\"data\":{\"id\":\"id\",\"name\":\"name\"}}}") - .put("dtm", "123456") .put("ttm", "456789") .put("aid", "cloudfront") - .build(), result); + .build(); + + assertTrue(result.entrySet().containsAll(expected.entrySet())); } @Test @@ -456,28 +472,26 @@ public void testTrackTiming() throws InterruptedException { .variable("variable") .timing(10) .customContext(contexts) - .deviceCreatedTimestamp(123456) .trueTimestamp(456789L) - .eventId(EXPECTED_EVENT_ID) .build()); // Then Thread.sleep(500); Map result = mockEmitter.eventList.get(0).getMap(); - assertEquals(ImmutableMap.builder() + Map expected = ImmutableMap.builder() .put("p", "srv") .put("tv", Version.TRACKER) .put("e", "ue") .put("co", EXPECTED_CONTEXTS) - .put("eid", EXPECTED_EVENT_ID) .put("tna", "AF003") .put("tz", "Etc/UTC") .put("ue_pr", "{\"schema\":\"iglu:com.snowplowanalytics.snowplow/unstruct_event/jsonschema/1-0-0\",\"data\":{\"schema\":\"iglu:com.snowplowanalytics.snowplow/timing/jsonschema/1-0-0\",\"data\":{\"category\":\"category\",\"label\":\"label\",\"timing\":10,\"variable\":\"variable\"}}}") - .put("dtm", "123456") .put("ttm", "456789") .put("aid", "cloudfront") - .build(), result); + .build(); + + assertTrue(result.entrySet().containsAll(expected.entrySet())); } @Test @@ -494,9 +508,7 @@ public void testTrackTimingWithSubject() throws InterruptedException { .variable("variable") .timing(10) .customContext(contexts) - .deviceCreatedTimestamp(123456) .trueTimestamp(456789L) - .eventId(EXPECTED_EVENT_ID) .subject(s1) .build()); @@ -504,20 +516,21 @@ public void testTrackTimingWithSubject() throws InterruptedException { Thread.sleep(500); Map result = mockEmitter.eventList.get(0).getMap(); - assertEquals(ImmutableMap.builder() + Map expected = ImmutableMap.builder() .put("p", "srv") .put("ue_pr", "{\"schema\":\"iglu:com.snowplowanalytics.snowplow/unstruct_event/jsonschema/1-0-0\",\"data\":{\"schema\":\"iglu:com.snowplowanalytics.snowplow/timing/jsonschema/1-0-0\",\"data\":{\"category\":\"category\",\"label\":\"label\",\"timing\":10,\"variable\":\"variable\"}}}") .put("tv", Version.TRACKER) .put("e", "ue") .put("ip", "127.0.0.1") .put("co", EXPECTED_CONTEXTS) - .put("eid", EXPECTED_EVENT_ID) .put("tna", "AF003") .put("tz", "Etc/UTC") - .put("dtm", "123456") .put("ttm", "456789") .put("aid", "cloudfront") - .build(), result); + .build(); + + assertTrue(result.entrySet().containsAll(expected.entrySet())); + } // --- Tracker Setter & Getter Tests @@ -538,17 +551,23 @@ public void testSetDefaultPlatform() { @Test public void testSetSubject() { + // Subject objects always have timezone set TimeZone.setDefault(TimeZone.getTimeZone("Etc/UTC")); + Subject s1 = new Subject.SubjectBuilder().build(); + s1.setLanguage("EN"); Tracker tracker = new Tracker.TrackerBuilder(mockEmitter, "AF003", "cloudfront") .subject(s1) .build(); + Subject s2 = new Subject.SubjectBuilder().build(); s2.setColorDepth(24); tracker.setSubject(s2); + Map subjectPairs = new HashMap<>(); subjectPairs.put("tz", "Etc/UTC"); subjectPairs.put("cd", "24"); + assertEquals(subjectPairs, tracker.getSubject().getSubject()); } diff --git a/src/test/java/com/snowplowanalytics/snowplow/tracker/emitter/BatchEmitterTest.java b/src/test/java/com/snowplowanalytics/snowplow/tracker/emitter/BatchEmitterTest.java index 5c310e41..2953c7f1 100644 --- a/src/test/java/com/snowplowanalytics/snowplow/tracker/emitter/BatchEmitterTest.java +++ b/src/test/java/com/snowplowanalytics/snowplow/tracker/emitter/BatchEmitterTest.java @@ -25,9 +25,6 @@ import org.junit.Before; import org.junit.Test; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; - import com.snowplowanalytics.snowplow.tracker.payload.SelfDescribingJson; import com.snowplowanalytics.snowplow.tracker.payload.TrackerPayload; import com.snowplowanalytics.snowplow.tracker.events.PageView; @@ -60,14 +57,10 @@ public int get(TrackerPayload payload) { } @Override - public String getUrl() { - return null; - } + public String getUrl() { return null; } @Override - public Object getHttpClient() { - return null; - } + public Object getHttpClient() { return null; } } // this class fails to "send" the first 4 requests @@ -87,19 +80,13 @@ public int post(SelfDescribingJson payload) { } @Override - public int get(TrackerPayload payload) { - return 0; - } + public int get(TrackerPayload payload) { return 0; } @Override - public String getUrl() { - return null; - } + public String getUrl() { return null; } @Override - public Object getHttpClient() { - return null; - } + public Object getHttpClient() { return null; } } @Before @@ -114,16 +101,15 @@ public void setUp() { @Test public void addToBuffer_withLess10Payloads_shouldNotEmptyBuffer() throws InterruptedException { - List payloads = createPayloads(2); - for (TrackerPayload payload : payloads) { - emitter.add(payload); - } + TrackerPayload payload = createPayload(); + boolean result = emitter.add(payload); Thread.sleep(500); + Assert.assertTrue(result); Assert.assertFalse(mockHttpClientAdapter.isPostCalled); - Assert.assertEquals(2, emitter.getBuffer().size()); - Assert.assertEquals(payloads, emitter.getBuffer()); + Assert.assertEquals(1, emitter.getBuffer().size()); + Assert.assertEquals(payload, emitter.getBuffer().get(0)); } @Test @@ -136,8 +122,6 @@ public void addToBuffer_withMore10Payloads_shouldEmptyBuffer() throws Interrupte Thread.sleep(500); Assert.assertTrue(mockHttpClientAdapter.isPostCalled); - @SuppressWarnings("unchecked") - List> capturedPayload = (List>) mockHttpClientAdapter.capturedPayload.getMap().get("data"); Assert.assertEquals(0, emitter.getBuffer().size()); Assert.assertEquals(1, mockHttpClientAdapter.postCounter); @@ -153,9 +137,10 @@ public void addToBuffer_doesNotAddEventIfBufferFull() { emitter.add(createPayload()); TrackerPayload differentPayload = createPayload(); - emitter.add(differentPayload); + boolean result = emitter.add(differentPayload); Assert.assertFalse(emitter.getBuffer().contains(differentPayload)); + Assert.assertFalse(result); } @Test @@ -358,10 +343,10 @@ private void assertPayload(List payloads, List