Skip to content

Commit

Permalink
feat: Implement event upload bypass option
Browse files Browse the repository at this point in the history
Allows clients to choose to pass events only to kits or (by default) also upload to mParticle's backend. This is chosen on an event-by-event basis by simply setting calling `shouldUploadEvent(false)` on the event builder (default `true` if `shouldUploadEvent()` isn't called).
  • Loading branch information
einsteinx2 authored and willpassidomo committed Aug 5, 2021
1 parent f4b0951 commit 3d8a967
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.mparticle

import com.mparticle.commerce.CommerceEvent
import com.mparticle.commerce.Product
import com.mparticle.networking.Matcher
import com.mparticle.testutils.BaseCleanStartedEachTest
import org.junit.Test
import kotlin.test.assertNotEquals

class UploadEventKotlinTest: BaseCleanStartedEachTest() {
@Test
fun testMPEventUploadBypass() {
val event = MPEvent.Builder("Should Not Upload")
.shouldUploadEvent(false)
.build()
val event2 = MPEvent.Builder("Should Upload 1")
.shouldUploadEvent(true)
.build()
val event3 = MPEvent.Builder("Should Upload 2")
.build()
MParticle.getInstance()?.logEvent(event)
MParticle.getInstance()?.logEvent(event2)
MParticle.getInstance()?.logEvent(event3)
MParticle.getInstance()?.upload()

// Wait for an event that matched Matcher"
// This matcher contains
// 1) a url (mServer.Endpoints().eventsUrl
// 2) a "body match" (bodyMatch {} )
//
// These 3 events are logged within the same upload loop, with the same mpid and sessionid, so they
// will be logged in the same upload message. This logic will basically wait until the "Should Upload"
// messages are received in an upload message and fail if that, or any previous message, contains the
// "Should Not Upload" message
var numUploadedEvents = 0
mServer.waitForVerify(Matcher(mServer.Endpoints().eventsUrl).bodyMatch {
it.optJSONArray("msgs")?.let { messagesArray ->
(0 until messagesArray.length())
.any {
val eventMessageName = messagesArray.getJSONObject(it).optString("n")
assertNotEquals("Should Not Upload", eventMessageName)
if (eventMessageName == "Should Upload 1" || eventMessageName == "Should Upload 2") {
numUploadedEvents++
}
numUploadedEvents == 2
}
} ?: false
})
}

@Test
fun testCommerceEventUploadBypass() {
val product = Product.Builder("Should Not Upload", "sku1", 100.00)
.build()
val event = CommerceEvent.Builder(Product.ADD_TO_CART, product)
.shouldUploadEvent(false)
.build()
var product2 = Product.Builder("Should Upload 1", "sku2", 100.00)
.build()
val event2 = CommerceEvent.Builder(Product.ADD_TO_CART, product2)
.shouldUploadEvent(true)
.build()
var product3 = Product.Builder("Should Upload 2", "sku3", 100.00)
.build()
val event3 = CommerceEvent.Builder(Product.ADD_TO_CART, product3)
.build()
MParticle.getInstance()?.logEvent(event)
MParticle.getInstance()?.logEvent(event2)
MParticle.getInstance()?.logEvent(event3)
MParticle.getInstance()?.upload()

// Wait for an event that matched Matcher"
// This matcher contains
// 1) a url (mServer.Endpoints().eventsUrl
// 2) a "body match" (bodyMatch {} )
//
// These 3 events are logged within the same upload loop, with the same mpid and sessionid, so they
// will be logged in the same upload message. This logic will basically wait until the "Should Upload"
// messages are received in an upload message and fail if that, or any previous message, contains the
// "Should Not Upload" message
var numUploadedEvents = 0
mServer.waitForVerify(Matcher(mServer.Endpoints().eventsUrl).bodyMatch {
it.optJSONArray("msgs")?.let { messagesArray ->
(0 until messagesArray.length())
.any {
val eventProductName = messagesArray.getJSONObject(it).optJSONObject("pd")?.optJSONArray("pl")?.optJSONObject(0)?.optString("nm")
assertNotEquals("Should Not Upload", eventProductName)
if (eventProductName == "Should Upload 1" || eventProductName == "Should Upload 2") {
numUploadedEvents++
}
numUploadedEvents == 2
}
} ?: false
})
}
}
9 changes: 9 additions & 0 deletions android-core/src/main/java/com/mparticle/BaseEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class BaseEvent {
private MessageType mType;
private Map<String, List<String>> mCustomFlags;
private Map<String, String> mCustomAttributes;
private boolean mShouldUploadEvent = true;

protected BaseEvent(MessageType type) {
mType = type;
Expand All @@ -31,6 +32,14 @@ protected void setType(MessageType type) {
this.mType = type;
}

public boolean isShouldUploadEvent() {
return mShouldUploadEvent;
}

protected void setShouldUploadEvent(boolean shouldUploadEvent) {
this.mShouldUploadEvent = shouldUploadEvent;
}

/**
* Retrieve the custom flags set on this event. Custom Flags are used to send data or trigger behavior
* to individual 3rd-party services that you have enabled for your app. By default, flags are not forwarded
Expand Down
32 changes: 29 additions & 3 deletions android-core/src/main/java/com/mparticle/MPEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ private MPEvent(Builder builder){
if (builder.customFlags != null) {
setCustomFlags(builder.customFlags);
}
if (builder.shouldUploadEvent != null) {
setShouldUploadEvent(builder.shouldUploadEvent);
}
screenEvent = builder.screenEvent;
}

Expand Down Expand Up @@ -126,6 +129,7 @@ public MPEvent(@NonNull MPEvent mpEvent) {
setCustomFlags(mpEvent.getCustomFlags());
entering = mpEvent.entering;
screenEvent = mpEvent.screenEvent;
setShouldUploadEvent(mpEvent.isShouldUploadEvent());
InternalListenerManager.getListener().onCompositeObjects(mpEvent, this);
}

Expand Down Expand Up @@ -251,6 +255,7 @@ public static class Builder {
private Double duration = null, startTime = null, endTime = null;
private Map<String, List<String>> customFlags = null;
private boolean entering = true;
private Boolean shouldUploadEvent;

private Builder(){}

Expand Down Expand Up @@ -299,6 +304,7 @@ public Builder(@NonNull MPEvent event) {
this.customFlags = event.getCustomFlags();
this.entering = event.entering;
this.screenEvent = event.screenEvent;
this.shouldUploadEvent = event.isShouldUploadEvent();
}

/**
Expand Down Expand Up @@ -452,7 +458,6 @@ public Builder endTime(){
}

/**
*
* Manually set the time when this event ended - should be epoch time milliseconds.
*
* Note that by using {@link #duration(double)}, this value will be ignored.
Expand All @@ -466,7 +471,6 @@ private Builder endTime(double endTimeMillis){
}

/**
*
* Beta API, subject to change. Used internally to signify if a user is entering or exiting a screen.
*
*
Expand All @@ -479,13 +483,28 @@ public Builder internalNavigationDirection(boolean entering){
return this;
}

/**
* Manually choose to skip uploading this event to mParticle server and only forward to kits.
*
* Note that if this method is not called, the default is to upload to mParticle as well as
* forward to kits to match the previous behavior.
*
* @param shouldUploadEvent
* @return returns this builder for easy method chaining
*/
@NonNull
public Builder shouldUploadEvent(boolean shouldUploadEvent) {
this.shouldUploadEvent = shouldUploadEvent;
return this;
}

/**
* Create the MPEvent. In development mode this method will throw an IllegalStateException if this
* MPEvent is invalid.
*
* @return returns the MPEvent object to be logged
*
* @see MParticle#logEvent(MPEvent)
* @see MParticle#logEvent(BaseEvent)
*/
@NonNull
public MPEvent build(){
Expand Down Expand Up @@ -540,6 +559,9 @@ public static Builder parseString(@NonNull String builderString){
}
builder.customFlags = cFlags;
}
if (json.has(EVENT_SHOULD_UPLOAD_EVENT)) {
builder.shouldUploadEvent = json.getBoolean(EVENT_SHOULD_UPLOAD_EVENT);
}

return builder;
}catch (Exception e){
Expand All @@ -556,6 +578,7 @@ public static Builder parseString(@NonNull String builderString){
private final static String EVENT_INFO = "customAttributes";
private final static String EVENT_START_TIME= "startTime";
private final static String EVENT_END_TIME= "endTime";
private final static String EVENT_SHOULD_UPLOAD_EVENT = "shouldUploadEvent";

/**
* Use this method to serialize an event builder to persist the object across app sessions. The JSON string
Expand Down Expand Up @@ -599,6 +622,9 @@ public String toString() {
}
jsonObject.put(EVENT_CUSTOM_FLAGS, flagsObject);
}
if (shouldUploadEvent != null) {
jsonObject.put(EVENT_SHOULD_UPLOAD_EVENT, shouldUploadEvent);
}
return jsonObject.toString();
}catch (JSONException jse){
Logger.warning("Failed to serialize MPEvent.Builder: " + jse.toString());
Expand Down
4 changes: 2 additions & 2 deletions android-core/src/main/java/com/mparticle/MParticle.java
Original file line number Diff line number Diff line change
Expand Up @@ -338,9 +338,9 @@ public String getInstallReferrer() {
}

public void logEvent(@NonNull BaseEvent event) {
if (event instanceof MPEvent) {
if (event instanceof MPEvent && event.isShouldUploadEvent()) {
logMPEvent((MPEvent)event);
} else if (event instanceof CommerceEvent) {
} else if (event instanceof CommerceEvent && event.isShouldUploadEvent()) {
logCommerceEvent((CommerceEvent)event);
} else {
if (mConfigManager.isEnabled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ private CommerceEvent(Builder builder) {
if (builder.mCustomFlags != null) {
setCustomFlags(builder.mCustomFlags);
}
if (builder.mShouldUploadEvent != null) {
setShouldUploadEvent(builder.mShouldUploadEvent);
}

if (MPUtility.isEmpty(mProductAction)
&& MPUtility.isEmpty(mPromotionAction)
Expand Down Expand Up @@ -124,7 +127,6 @@ private CommerceEvent(Builder builder) {
}
}


if (mTransactionAttributes == null || mTransactionAttributes.getRevenue() == null) {
double transactionRevenue = 0;
if (mTransactionAttributes == null) {
Expand Down Expand Up @@ -513,6 +515,7 @@ public static class Builder {
private List<Impression> mImpressions;
private String mEventName;
private Map<String, List<String>> mCustomFlags = null;
private Boolean mShouldUploadEvent = null;

private Builder() {
mProductAction = mPromotionAction = null;
Expand Down Expand Up @@ -609,6 +612,7 @@ public Builder(@NonNull CommerceEvent event) {
}
mEventName = event.getEventName();
mCustomFlags = event.getCustomFlags();
mShouldUploadEvent = event.isShouldUploadEvent();
}

/**
Expand Down Expand Up @@ -869,5 +873,20 @@ public Builder internalEventName(@Nullable String eventName) {
mEventName = eventName;
return this;
}

/**
* Manually choose to skip uploading this event to mParticle server and only forward to kits.
*
* Note that if this method is not called, the default is to upload to mParticle as well as
* forward to kits to match the previous behavior.
*
* @param shouldUploadEvent
* @return returns this builder for easy method chaining
*/
@NonNull
public Builder shouldUploadEvent(boolean shouldUploadEvent) {
mShouldUploadEvent = shouldUploadEvent;
return this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public static List<MPEvent> expandProductAction(CommerceEvent event) {
attributes.putAll(event.getCustomAttributes());
}
extractActionAttributes(event, attributes);
events.add(plusOne.customAttributes(attributes).build());
events.add(plusOne.customAttributes(attributes).shouldUploadEvent(event.isShouldUploadEvent()).build());
}
List<Product> products = event.getProducts();
if (products != null) {
Expand All @@ -62,7 +62,7 @@ public static List<MPEvent> expandProductAction(CommerceEvent event) {
extractProductFields(products.get(i), attributeExtracted);
extractProductAttributes(products.get(i), attributeExtracted);
extractTransactionId(event, attributeExtracted);
events.add(itemEvent.customAttributes(attributes).build());
events.add(itemEvent.customAttributes(attributes).shouldUploadEvent(event.isShouldUploadEvent()).build());
}
}
return events;
Expand Down Expand Up @@ -190,7 +190,7 @@ public static List<MPEvent> expandPromotionAction(CommerceEvent event) {
attributes.putAll(event.getCustomAttributes());
}
extractPromotionAttributes(promotions.get(i), attributes);
events.add(itemEvent.customAttributes(attributes).build());
events.add(itemEvent.customAttributes(attributes).shouldUploadEvent(event.isShouldUploadEvent()).build());
}
}
return events;
Expand Down Expand Up @@ -235,7 +235,7 @@ public static List<MPEvent> expandProductImpression(CommerceEvent event) {
extractProductAttributes(products.get(i), attributes);
extractProductFields(products.get(i), attributes);
extractImpressionAttributes(impressions.get(i), attributes);
events.add(itemEvent.customAttributes(attributes).build());
events.add(itemEvent.customAttributes(attributes).shouldUploadEvent(event.isShouldUploadEvent()).build());
}
}
}
Expand Down

0 comments on commit 3d8a967

Please sign in to comment.