Skip to content

Commit

Permalink
Allow self-describing event data
Browse files Browse the repository at this point in the history
  • Loading branch information
mscwilson committed Nov 27, 2024
1 parent d046ec7 commit b0d2012
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ package com.snowplowanalytics.snowplow.tracker
import android.content.Context
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Param
import com.snowplowanalytics.core.constants.Parameters
import com.snowplowanalytics.core.constants.TrackerConstants
import com.snowplowanalytics.core.emitter.Executor
import com.snowplowanalytics.core.tracker.Tracker
import com.snowplowanalytics.core.tracker.TrackerWebViewInterfaceV2
import com.snowplowanalytics.snowplow.Snowplow.createTracker
import com.snowplowanalytics.snowplow.Snowplow.removeAllTrackers
Expand Down Expand Up @@ -59,18 +61,26 @@ class TrackerWebViewInterfaceV2Test {

@Test
@Throws(JSONException::class, InterruptedException::class)
fun tracksPagePingEvent() {
fun tracksEventWithAllOptions() {
val data = "{\"schema\":\"iglu:etc\",\"data\":{\"key\":\"val\"}}"

webInterface!!.trackWebViewEvent(
eventName = "pp",
eventName = "ue",
trackerVersion = "webview",
useragent = "Firefox",
useragent = "Chrome",
selfDescribingEventData = data,
pageUrl = "http://snowplow.com",
pageTitle = "Snowplow",
referrer = "http://google.com",
pingXOffsetMin = 10,
pingXOffsetMax = 20,
pingYOffsetMin = 30,
pingYOffsetMax = 40
pingYOffsetMax = 40,
category = "cat",
action = "act",
property = "prop",
label = "lbl",
value = 10.0
)

Thread.sleep(200)
Expand All @@ -80,69 +90,27 @@ class TrackerWebViewInterfaceV2Test {

val request = networkConnection.allRequests[0]
val payload = request.payload.map
assertEquals("pp", payload[Parameters.EVENT])

assertEquals("ue", payload[Parameters.EVENT])
assertEquals("webview", payload[Parameters.TRACKER_VERSION])
assertEquals("Firefox", payload[Parameters.USERAGENT])
assertEquals("Chrome", payload[Parameters.USERAGENT])
assertEquals("http://snowplow.com", payload[Parameters.PAGE_URL])
assertEquals("Snowplow", payload[Parameters.PAGE_TITLE])
assertEquals("http://google.com", payload[Parameters.PAGE_REFR])
assertEquals("10", payload[Parameters.PING_XOFFSET_MIN])
assertEquals("20", payload[Parameters.PING_XOFFSET_MAX])
assertEquals("30", payload[Parameters.PING_YOFFSET_MIN])
assertEquals("40", payload[Parameters.PING_YOFFSET_MAX])
}

@Test
@Throws(JSONException::class, InterruptedException::class)
fun tracksStructuredEvent() {
webInterface!!.trackWebViewEvent(
eventName = "se",
trackerVersion = "webview2",
useragent = "Firefox",
category = "cat",
action = "act",
property = "prop",
label = "lbl",
value = 10.0
)

Thread.sleep(200)
waitForEvents(networkConnection, 1)

assertEquals(1, networkConnection.countRequests())

val request = networkConnection.allRequests[0]
val payload = request.payload.map

assertEquals("se", payload[Parameters.EVENT])
assertEquals("webview2", payload[Parameters.TRACKER_VERSION])
assertEquals("Firefox", payload[Parameters.USERAGENT])
assertEquals("cat", payload[Parameters.SE_CATEGORY])
assertEquals("act", payload[Parameters.SE_ACTION])
assertEquals("prop", payload[Parameters.SE_PROPERTY])
assertEquals("lbl", payload[Parameters.SE_LABEL])
assertEquals("10.0", payload[Parameters.SE_VALUE])
}

@Test
@Throws(JSONException::class, InterruptedException::class)
fun tracksSelfDescribingEvent() {
// val data = "[{\"schema\":\"http://schema.com\",\"data\":{\"key\":\"val\"}}]"
// webInterface!!.trackWebViewEvent(
// eventName = "ue",
// trackerVersion = "webview2",
// useragent = "Firefox",
// selfDescribingEventData = data
// )
//
// Thread.sleep(200)
//
// assertEquals(1, trackedEvents.size)
// assertEquals("webViewEvent", trackedEvents.first().name)
//
// val payload = trackedEvents.first().payload
// assertEquals(data, payload["changeThis"])

assertTrue(payload.containsKey(Parameters.UNSTRUCTURED))
val selfDescJson = JSONObject(payload[Parameters.UNSTRUCTURED] as String)
assertEquals(TrackerConstants.SCHEMA_UNSTRUCT_EVENT, selfDescJson.getString("schema"))
assertEquals(data, selfDescJson.getString("data"))
}

@Test
Expand Down Expand Up @@ -171,6 +139,8 @@ class TrackerWebViewInterfaceV2Test {
assertEquals(0, networkConnection.countRequests())
assertEquals(1, networkConnection2.countRequests())

assertEquals("pv", networkConnection2.allRequests[0].payload.map[Parameters.EVENT])

// tracks using default tracker if not specified
webInterface!!.trackWebViewEvent(
eventName = "pp",
Expand Down Expand Up @@ -202,7 +172,7 @@ class TrackerWebViewInterfaceV2Test {
assertEquals(1, networkConnection.countRequests())

val relevantEntities = ArrayList<JSONObject>()
val allEntities = JSONObject(networkConnection.allRequests[0].payload.map["co"] as String)
val allEntities = JSONObject(networkConnection.allRequests[0].payload.map[Parameters.CONTEXT] as String)
.getJSONArray("data")
for (i in 0 until allEntities.length()) {
if (allEntities.getJSONObject(i).getString("schema") == "iglu:com.example/etc") {
Expand All @@ -212,6 +182,13 @@ class TrackerWebViewInterfaceV2Test {
assertEquals(1, relevantEntities.size)
assertEquals("val", relevantEntities[0].get("key") as? String)
}

@Test
@Throws(JSONException::class, InterruptedException::class)
fun addsEventNameAndSchemaForInspection() {

// TODO
}

// --- PRIVATE
private val context: Context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,4 +265,5 @@ object Parameters {
const val PING_XOFFSET_MAX = "pp_max"
const val PING_YOFFSET_MIN = "pp_miy"
const val PING_YOFFSET_MAX = "pp_may"
const val WEBVIEW_EVENT_DATA = "selfDescribingEventData"
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ package com.snowplowanalytics.core.event

import com.snowplowanalytics.core.constants.Parameters
import com.snowplowanalytics.snowplow.event.AbstractEvent
import com.snowplowanalytics.snowplow.payload.SelfDescribingJson

/**
* A PageView event. This event has been designed for web trackers, and is not suitable for mobile apps.
* @param pageUrl The page URL.
* Allows the tracking of JavaScript events from WebViews.
*/
class WebViewReader(
val eventName: String,
val trackerVersion: String,
val useragent: String,
val selfDescribingEventData: String? = null,
val selfDescribingEventData: SelfDescribingJson? = null,
val pageUrl: String? = null,
val pageTitle: String? = null,
val referrer: String? = null,
Expand All @@ -47,7 +47,7 @@ class WebViewReader(
payload[Parameters.TRACKER_VERSION] = trackerVersion
payload[Parameters.USERAGENT] = useragent

if (selfDescribingEventData != null) payload["selfDescribingEventData"] = selfDescribingEventData
if (selfDescribingEventData != null) payload[Parameters.WEBVIEW_EVENT_DATA] = selfDescribingEventData

if (pageUrl != null) payload[Parameters.PAGE_URL] = pageUrl
if (pageTitle != null) payload[Parameters.PAGE_TITLE] = pageTitle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,20 @@ class TrackerEvent @JvmOverloads constructor(event: Event, state: TrackerStateSn
}

isService = event is TrackerError
if (event is WebViewReader) {
name = payload[Parameters.EVENT]?.toString()
isPrimitive = true
isWebView = true
} else if (event is AbstractPrimitive) {
name = event.name
isPrimitive = true
} else {
schema = (event as? AbstractSelfDescribing)?.schema
isPrimitive = false
when (event) {
is WebViewReader -> {
name = payload[Parameters.EVENT]?.toString()
schema = getWebViewSchema()
isWebView = true
}
is AbstractPrimitive -> {
name = event.name
isPrimitive = true
}
else -> {
schema = (event as? AbstractSelfDescribing)?.schema
isPrimitive = false
}
}
}

Expand Down Expand Up @@ -106,16 +110,19 @@ class TrackerEvent @JvmOverloads constructor(event: Event, state: TrackerStateSn
}

fun wrapPropertiesToPayload(toPayload: Payload, base64Encoded: Boolean) {
if (isPrimitive) {
toPayload.addMap(payload)
} else {
wrapSelfDescribingToPayload(toPayload, base64Encoded)
when {
isWebView -> wrapWebViewToPayload(toPayload, base64Encoded)
isPrimitive -> toPayload.addMap(payload)
else -> wrapSelfDescribingEventToPayload(toPayload, base64Encoded)
}
}

private fun getWebViewSchema(): String? {
val selfDescribingData = payload[Parameters.WEBVIEW_EVENT_DATA] as SelfDescribingJson?
return selfDescribingData?.map?.get(Parameters.SCHEMA)?.toString()
}

private fun wrapSelfDescribingToPayload(toPayload: Payload, base64Encoded: Boolean) {
val schema = schema ?: return
val data = SelfDescribingJson(schema, payload)
private fun addSelfDescribingDataToPayload(toPayload: Payload, base64Encoded: Boolean, data: SelfDescribingJson) {
val unstructuredEventPayload = HashMap<String?, Any?>()
unstructuredEventPayload[Parameters.SCHEMA] = TrackerConstants.SCHEMA_UNSTRUCT_EVENT
unstructuredEventPayload[Parameters.DATA] = data.map
Expand All @@ -126,4 +133,18 @@ class TrackerEvent @JvmOverloads constructor(event: Event, state: TrackerStateSn
Parameters.UNSTRUCTURED
)
}

private fun wrapWebViewToPayload(toPayload: Payload, base64Encoded: Boolean) {
val selfDescribingData = payload[Parameters.WEBVIEW_EVENT_DATA] as SelfDescribingJson?
if (selfDescribingData != null) {
addSelfDescribingDataToPayload(toPayload, base64Encoded, selfDescribingData)
}
payload.remove(Parameters.WEBVIEW_EVENT_DATA)
toPayload.addMap(payload)
}

private fun wrapSelfDescribingEventToPayload(toPayload: Payload, base64Encoded: Boolean) {
val schema = schema ?: return
addSelfDescribingDataToPayload(toPayload, base64Encoded, SelfDescribingJson(schema, payload))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.snowplowanalytics.snowplow.event.*
import com.snowplowanalytics.snowplow.payload.SelfDescribingJson
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.util.*

/**
Expand Down Expand Up @@ -54,7 +55,7 @@ class TrackerWebViewInterfaceV2 {
eventName,
trackerVersion,
useragent,
selfDescribingEventData,
parseSelfDescribingEventData(selfDescribingEventData),
pageUrl,
pageTitle,
referrer,
Expand Down Expand Up @@ -98,22 +99,41 @@ class TrackerWebViewInterfaceV2 {
}
}

@Throws(JSONException::class)
private fun createSelfDescribingJson(map: Map<String, Any?>): SelfDescribingJson? {
val schema = map["schema"] as? String?
val data = map["data"]
return if (schema != null && data != null) {
SelfDescribingJson(schema, data)
} else {
null
}
}

@Throws(JSONException::class)
private fun parseEntities(serialisedEntities: String): List<SelfDescribingJson> {
val entities: MutableList<SelfDescribingJson> = ArrayList()
val contextJson = JSONArray(serialisedEntities)
for (i in 0 until contextJson.length()) {
val itemJson = contextJson.getJSONObject(i)
val entitiesJson = JSONArray(serialisedEntities)
for (i in 0 until entitiesJson.length()) {
val itemJson = entitiesJson.getJSONObject(i)
val item = jsonToMap(itemJson)
val schema = item["schema"] as? String?
val data = item["data"]
if (schema != null && data != null) {
entities.add(SelfDescribingJson(schema, data))
val selfDescribingJson = createSelfDescribingJson(item)

if (selfDescribingJson != null) {
entities.add(selfDescribingJson)
}
}
return entities
}

@Throws(JSONException::class)
private fun parseSelfDescribingEventData(serialisedEvent: String?): SelfDescribingJson? {
return serialisedEvent?.let {
val eventJson = JSONObject(it)
createSelfDescribingJson(jsonToMap(eventJson))
}
}

companion object {
const val TAG = "SnowplowWebInterfaceV2"
}
Expand Down

0 comments on commit b0d2012

Please sign in to comment.