Skip to content

Commit b0d2012

Browse files
committed
Allow self-describing event data
1 parent d046ec7 commit b0d2012

File tree

5 files changed

+103
-84
lines changed

5 files changed

+103
-84
lines changed

snowplow-tracker/src/androidTest/java/com/snowplowanalytics/snowplow/tracker/TrackerWebViewInterfaceV2Test.kt

Lines changed: 32 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ package com.snowplowanalytics.snowplow.tracker
1616
import android.content.Context
1717
import androidx.test.ext.junit.runners.AndroidJUnit4
1818
import androidx.test.platform.app.InstrumentationRegistry
19+
import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Param
1920
import com.snowplowanalytics.core.constants.Parameters
2021
import com.snowplowanalytics.core.constants.TrackerConstants
2122
import com.snowplowanalytics.core.emitter.Executor
23+
import com.snowplowanalytics.core.tracker.Tracker
2224
import com.snowplowanalytics.core.tracker.TrackerWebViewInterfaceV2
2325
import com.snowplowanalytics.snowplow.Snowplow.createTracker
2426
import com.snowplowanalytics.snowplow.Snowplow.removeAllTrackers
@@ -59,18 +61,26 @@ class TrackerWebViewInterfaceV2Test {
5961

6062
@Test
6163
@Throws(JSONException::class, InterruptedException::class)
62-
fun tracksPagePingEvent() {
64+
fun tracksEventWithAllOptions() {
65+
val data = "{\"schema\":\"iglu:etc\",\"data\":{\"key\":\"val\"}}"
66+
6367
webInterface!!.trackWebViewEvent(
64-
eventName = "pp",
68+
eventName = "ue",
6569
trackerVersion = "webview",
66-
useragent = "Firefox",
70+
useragent = "Chrome",
71+
selfDescribingEventData = data,
6772
pageUrl = "http://snowplow.com",
6873
pageTitle = "Snowplow",
6974
referrer = "http://google.com",
7075
pingXOffsetMin = 10,
7176
pingXOffsetMax = 20,
7277
pingYOffsetMin = 30,
73-
pingYOffsetMax = 40
78+
pingYOffsetMax = 40,
79+
category = "cat",
80+
action = "act",
81+
property = "prop",
82+
label = "lbl",
83+
value = 10.0
7484
)
7585

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

8191
val request = networkConnection.allRequests[0]
8292
val payload = request.payload.map
83-
84-
assertEquals("pp", payload[Parameters.EVENT])
93+
94+
assertEquals("ue", payload[Parameters.EVENT])
8595
assertEquals("webview", payload[Parameters.TRACKER_VERSION])
86-
assertEquals("Firefox", payload[Parameters.USERAGENT])
96+
assertEquals("Chrome", payload[Parameters.USERAGENT])
8797
assertEquals("http://snowplow.com", payload[Parameters.PAGE_URL])
8898
assertEquals("Snowplow", payload[Parameters.PAGE_TITLE])
8999
assertEquals("http://google.com", payload[Parameters.PAGE_REFR])
90100
assertEquals("10", payload[Parameters.PING_XOFFSET_MIN])
91101
assertEquals("20", payload[Parameters.PING_XOFFSET_MAX])
92102
assertEquals("30", payload[Parameters.PING_YOFFSET_MIN])
93103
assertEquals("40", payload[Parameters.PING_YOFFSET_MAX])
94-
}
95-
96-
@Test
97-
@Throws(JSONException::class, InterruptedException::class)
98-
fun tracksStructuredEvent() {
99-
webInterface!!.trackWebViewEvent(
100-
eventName = "se",
101-
trackerVersion = "webview2",
102-
useragent = "Firefox",
103-
category = "cat",
104-
action = "act",
105-
property = "prop",
106-
label = "lbl",
107-
value = 10.0
108-
)
109-
110-
Thread.sleep(200)
111-
waitForEvents(networkConnection, 1)
112-
113-
assertEquals(1, networkConnection.countRequests())
114-
115-
val request = networkConnection.allRequests[0]
116-
val payload = request.payload.map
117-
118-
assertEquals("se", payload[Parameters.EVENT])
119-
assertEquals("webview2", payload[Parameters.TRACKER_VERSION])
120-
assertEquals("Firefox", payload[Parameters.USERAGENT])
121104
assertEquals("cat", payload[Parameters.SE_CATEGORY])
122105
assertEquals("act", payload[Parameters.SE_ACTION])
123106
assertEquals("prop", payload[Parameters.SE_PROPERTY])
124107
assertEquals("lbl", payload[Parameters.SE_LABEL])
125108
assertEquals("10.0", payload[Parameters.SE_VALUE])
126-
}
127-
128-
@Test
129-
@Throws(JSONException::class, InterruptedException::class)
130-
fun tracksSelfDescribingEvent() {
131-
// val data = "[{\"schema\":\"http://schema.com\",\"data\":{\"key\":\"val\"}}]"
132-
// webInterface!!.trackWebViewEvent(
133-
// eventName = "ue",
134-
// trackerVersion = "webview2",
135-
// useragent = "Firefox",
136-
// selfDescribingEventData = data
137-
// )
138-
//
139-
// Thread.sleep(200)
140-
//
141-
// assertEquals(1, trackedEvents.size)
142-
// assertEquals("webViewEvent", trackedEvents.first().name)
143-
//
144-
// val payload = trackedEvents.first().payload
145-
// assertEquals(data, payload["changeThis"])
109+
110+
assertTrue(payload.containsKey(Parameters.UNSTRUCTURED))
111+
val selfDescJson = JSONObject(payload[Parameters.UNSTRUCTURED] as String)
112+
assertEquals(TrackerConstants.SCHEMA_UNSTRUCT_EVENT, selfDescJson.getString("schema"))
113+
assertEquals(data, selfDescJson.getString("data"))
146114
}
147115

148116
@Test
@@ -171,6 +139,8 @@ class TrackerWebViewInterfaceV2Test {
171139
assertEquals(0, networkConnection.countRequests())
172140
assertEquals(1, networkConnection2.countRequests())
173141

142+
assertEquals("pv", networkConnection2.allRequests[0].payload.map[Parameters.EVENT])
143+
174144
// tracks using default tracker if not specified
175145
webInterface!!.trackWebViewEvent(
176146
eventName = "pp",
@@ -202,7 +172,7 @@ class TrackerWebViewInterfaceV2Test {
202172
assertEquals(1, networkConnection.countRequests())
203173

204174
val relevantEntities = ArrayList<JSONObject>()
205-
val allEntities = JSONObject(networkConnection.allRequests[0].payload.map["co"] as String)
175+
val allEntities = JSONObject(networkConnection.allRequests[0].payload.map[Parameters.CONTEXT] as String)
206176
.getJSONArray("data")
207177
for (i in 0 until allEntities.length()) {
208178
if (allEntities.getJSONObject(i).getString("schema") == "iglu:com.example/etc") {
@@ -212,6 +182,13 @@ class TrackerWebViewInterfaceV2Test {
212182
assertEquals(1, relevantEntities.size)
213183
assertEquals("val", relevantEntities[0].get("key") as? String)
214184
}
185+
186+
@Test
187+
@Throws(JSONException::class, InterruptedException::class)
188+
fun addsEventNameAndSchemaForInspection() {
189+
190+
// TODO
191+
}
215192

216193
// --- PRIVATE
217194
private val context: Context

snowplow-tracker/src/main/java/com/snowplowanalytics/core/constants/Parameters.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,4 +265,5 @@ object Parameters {
265265
const val PING_XOFFSET_MAX = "pp_max"
266266
const val PING_YOFFSET_MIN = "pp_miy"
267267
const val PING_YOFFSET_MAX = "pp_may"
268+
const val WEBVIEW_EVENT_DATA = "selfDescribingEventData"
268269
}

snowplow-tracker/src/main/java/com/snowplowanalytics/core/event/WebViewReader.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@ package com.snowplowanalytics.core.event
1414

1515
import com.snowplowanalytics.core.constants.Parameters
1616
import com.snowplowanalytics.snowplow.event.AbstractEvent
17+
import com.snowplowanalytics.snowplow.payload.SelfDescribingJson
1718

1819
/**
19-
* A PageView event. This event has been designed for web trackers, and is not suitable for mobile apps.
20-
* @param pageUrl The page URL.
20+
* Allows the tracking of JavaScript events from WebViews.
2121
*/
2222
class WebViewReader(
2323
val eventName: String,
2424
val trackerVersion: String,
2525
val useragent: String,
26-
val selfDescribingEventData: String? = null,
26+
val selfDescribingEventData: SelfDescribingJson? = null,
2727
val pageUrl: String? = null,
2828
val pageTitle: String? = null,
2929
val referrer: String? = null,
@@ -47,7 +47,7 @@ class WebViewReader(
4747
payload[Parameters.TRACKER_VERSION] = trackerVersion
4848
payload[Parameters.USERAGENT] = useragent
4949

50-
if (selfDescribingEventData != null) payload["selfDescribingEventData"] = selfDescribingEventData
50+
if (selfDescribingEventData != null) payload[Parameters.WEBVIEW_EVENT_DATA] = selfDescribingEventData
5151

5252
if (pageUrl != null) payload[Parameters.PAGE_URL] = pageUrl
5353
if (pageTitle != null) payload[Parameters.PAGE_TITLE] = pageTitle

snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/TrackerEvent.kt

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,20 @@ class TrackerEvent @JvmOverloads constructor(event: Event, state: TrackerStateSn
5858
}
5959

6060
isService = event is TrackerError
61-
if (event is WebViewReader) {
62-
name = payload[Parameters.EVENT]?.toString()
63-
isPrimitive = true
64-
isWebView = true
65-
} else if (event is AbstractPrimitive) {
66-
name = event.name
67-
isPrimitive = true
68-
} else {
69-
schema = (event as? AbstractSelfDescribing)?.schema
70-
isPrimitive = false
61+
when (event) {
62+
is WebViewReader -> {
63+
name = payload[Parameters.EVENT]?.toString()
64+
schema = getWebViewSchema()
65+
isWebView = true
66+
}
67+
is AbstractPrimitive -> {
68+
name = event.name
69+
isPrimitive = true
70+
}
71+
else -> {
72+
schema = (event as? AbstractSelfDescribing)?.schema
73+
isPrimitive = false
74+
}
7175
}
7276
}
7377

@@ -106,16 +110,19 @@ class TrackerEvent @JvmOverloads constructor(event: Event, state: TrackerStateSn
106110
}
107111

108112
fun wrapPropertiesToPayload(toPayload: Payload, base64Encoded: Boolean) {
109-
if (isPrimitive) {
110-
toPayload.addMap(payload)
111-
} else {
112-
wrapSelfDescribingToPayload(toPayload, base64Encoded)
113+
when {
114+
isWebView -> wrapWebViewToPayload(toPayload, base64Encoded)
115+
isPrimitive -> toPayload.addMap(payload)
116+
else -> wrapSelfDescribingEventToPayload(toPayload, base64Encoded)
113117
}
114118
}
119+
120+
private fun getWebViewSchema(): String? {
121+
val selfDescribingData = payload[Parameters.WEBVIEW_EVENT_DATA] as SelfDescribingJson?
122+
return selfDescribingData?.map?.get(Parameters.SCHEMA)?.toString()
123+
}
115124

116-
private fun wrapSelfDescribingToPayload(toPayload: Payload, base64Encoded: Boolean) {
117-
val schema = schema ?: return
118-
val data = SelfDescribingJson(schema, payload)
125+
private fun addSelfDescribingDataToPayload(toPayload: Payload, base64Encoded: Boolean, data: SelfDescribingJson) {
119126
val unstructuredEventPayload = HashMap<String?, Any?>()
120127
unstructuredEventPayload[Parameters.SCHEMA] = TrackerConstants.SCHEMA_UNSTRUCT_EVENT
121128
unstructuredEventPayload[Parameters.DATA] = data.map
@@ -126,4 +133,18 @@ class TrackerEvent @JvmOverloads constructor(event: Event, state: TrackerStateSn
126133
Parameters.UNSTRUCTURED
127134
)
128135
}
136+
137+
private fun wrapWebViewToPayload(toPayload: Payload, base64Encoded: Boolean) {
138+
val selfDescribingData = payload[Parameters.WEBVIEW_EVENT_DATA] as SelfDescribingJson?
139+
if (selfDescribingData != null) {
140+
addSelfDescribingDataToPayload(toPayload, base64Encoded, selfDescribingData)
141+
}
142+
payload.remove(Parameters.WEBVIEW_EVENT_DATA)
143+
toPayload.addMap(payload)
144+
}
145+
146+
private fun wrapSelfDescribingEventToPayload(toPayload: Payload, base64Encoded: Boolean) {
147+
val schema = schema ?: return
148+
addSelfDescribingDataToPayload(toPayload, base64Encoded, SelfDescribingJson(schema, payload))
149+
}
129150
}

snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/TrackerWebViewInterfaceV2.kt

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import com.snowplowanalytics.snowplow.event.*
2222
import com.snowplowanalytics.snowplow.payload.SelfDescribingJson
2323
import org.json.JSONArray
2424
import org.json.JSONException
25+
import org.json.JSONObject
2526
import java.util.*
2627

2728
/**
@@ -54,7 +55,7 @@ class TrackerWebViewInterfaceV2 {
5455
eventName,
5556
trackerVersion,
5657
useragent,
57-
selfDescribingEventData,
58+
parseSelfDescribingEventData(selfDescribingEventData),
5859
pageUrl,
5960
pageTitle,
6061
referrer,
@@ -98,22 +99,41 @@ class TrackerWebViewInterfaceV2 {
9899
}
99100
}
100101

102+
@Throws(JSONException::class)
103+
private fun createSelfDescribingJson(map: Map<String, Any?>): SelfDescribingJson? {
104+
val schema = map["schema"] as? String?
105+
val data = map["data"]
106+
return if (schema != null && data != null) {
107+
SelfDescribingJson(schema, data)
108+
} else {
109+
null
110+
}
111+
}
112+
101113
@Throws(JSONException::class)
102114
private fun parseEntities(serialisedEntities: String): List<SelfDescribingJson> {
103115
val entities: MutableList<SelfDescribingJson> = ArrayList()
104-
val contextJson = JSONArray(serialisedEntities)
105-
for (i in 0 until contextJson.length()) {
106-
val itemJson = contextJson.getJSONObject(i)
116+
val entitiesJson = JSONArray(serialisedEntities)
117+
for (i in 0 until entitiesJson.length()) {
118+
val itemJson = entitiesJson.getJSONObject(i)
107119
val item = jsonToMap(itemJson)
108-
val schema = item["schema"] as? String?
109-
val data = item["data"]
110-
if (schema != null && data != null) {
111-
entities.add(SelfDescribingJson(schema, data))
120+
val selfDescribingJson = createSelfDescribingJson(item)
121+
122+
if (selfDescribingJson != null) {
123+
entities.add(selfDescribingJson)
112124
}
113125
}
114126
return entities
115127
}
116128

129+
@Throws(JSONException::class)
130+
private fun parseSelfDescribingEventData(serialisedEvent: String?): SelfDescribingJson? {
131+
return serialisedEvent?.let {
132+
val eventJson = JSONObject(it)
133+
createSelfDescribingJson(jsonToMap(eventJson))
134+
}
135+
}
136+
117137
companion object {
118138
const val TAG = "SnowplowWebInterfaceV2"
119139
}

0 commit comments

Comments
 (0)