Skip to content

Commit 0c24f48

Browse files
authored
[CDX-487] Add trackSearchSubmit that supports analytics tags (#173)
* overload trackSearchSubmit with new builder param * address comments * Rename class * Fix docs and readme * docs
1 parent 8f47ae1 commit 0c24f48

7 files changed

Lines changed: 109 additions & 6 deletions

File tree

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,13 @@ ConstructorIo.trackAutocompleteSelect("Fashionable Toothpicks", "tooth", "Produc
529529

530530
// Track when the user submits a search (searchTerm, originalQuery)
531531
ConstructorIo.trackSearchSubmit("toothpicks", "tooth")
532+
533+
// Track when the user submits a search with additional parameters, i.e. analytics tags
534+
// Request level analytics tags are merged with the default analytics tags passed on initialization
535+
val request = SearchSubmitTrackingData.build("toothpicks", "tooth") {
536+
setAnalyticsTags(mapOf("relatedSearchTerm" to "true"))
537+
}
538+
ConstructorIo.trackSearchSubmit(request)
532539
```
533540

534541
### Search Events

library/src/main/java/io/constructor/core/Constants.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ class Constants {
6464
const val GROUPS_MAX_DEPTH = "groups_max_depth"
6565
const val FILTER_GROUP_ID = "filters[group_id]"
6666
const val PRE_FILTER_EXPRESSION = "pre_filter_expression"
67+
const val ANALYTICS_TAGS = "analytics_tags[%s]"
6768
}
6869

6970
object QueryValues {

library/src/main/java/io/constructor/core/ConstructorIo.kt

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1490,9 +1490,38 @@ object ConstructorIo {
14901490
t -> e("Search Submit error: ${t.message}")
14911491
}))
14921492
}
1493-
internal fun trackSearchSubmitInternal(searchTerm: String, originalQuery: String, resultGroup: ResultGroup?): Completable {
1493+
1494+
/**
1495+
* Tracks search submit events.
1496+
*
1497+
* Example:
1498+
* ```
1499+
* val request = SearchSubmitTrackingData.build("toothpicks", "tooth") {
1500+
* setResultGroup(ResultGroup("Canned Goods", "canned-goods"))
1501+
* setAnalyticsTags(mapOf("relatedSearchTerm" to "true"))
1502+
* }
1503+
* ConstructorIo.trackSearchSubmit(request)
1504+
* ```
1505+
* @param request the search submit request object holding all the tracking parameters. Any
1506+
* analytics tags set on it are merged with the default analytics tags set on
1507+
* [ConstructorIoConfig.defaultAnalyticsTags], with request-level values overriding defaults on
1508+
* key collision.
1509+
*/
1510+
fun trackSearchSubmit(request: SearchSubmitTrackingData) {
1511+
val completable = trackSearchSubmitInternal(request.searchTerm, request.originalQuery, request.resultGroup, request.analyticsTags)
1512+
disposable.add(completable.subscribeOn(Schedulers.io()).subscribe({
1513+
context.broadcastIntent(Constants.EVENT_QUERY_SENT, Constants.EXTRA_TERM to request.searchTerm)
1514+
}, {
1515+
t -> e("Search Submit error: ${t.message}")
1516+
}))
1517+
}
1518+
1519+
internal fun trackSearchSubmitInternal(searchTerm: String, originalQuery: String, resultGroup: ResultGroup?, analyticsTags: Map<String, String>? = null): Completable {
14941520
preferenceHelper.getSessionId(sessionIncrementHandler)
14951521
val encodedParams: ArrayList<Pair<String, String>> = getEncodedParams(groupId = resultGroup?.groupId, groupDisplayName = resultGroup?.displayName)
1522+
mergeAnalyticsTags(configMemoryHolder.defaultAnalyticsTags, analyticsTags)?.forEach { (key, value) ->
1523+
encodedParams.add(Constants.QueryConstants.ANALYTICS_TAGS.format(key).urlEncode() to value.urlEncode())
1524+
}
14961525

14971526
return dataManager.trackSearchSubmit(searchTerm, arrayOf(
14981527
Constants.QueryConstants.ORIGINAL_QUERY to originalQuery,
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package io.constructor.data.builder
2+
3+
import io.constructor.data.model.common.ResultGroup
4+
5+
/**
6+
* Create a Search Submit tracking request object utilizing a builder
7+
*/
8+
class SearchSubmitTrackingData(
9+
val searchTerm: String,
10+
val originalQuery: String,
11+
val resultGroup: ResultGroup? = null,
12+
val analyticsTags: Map<String, String>? = null,
13+
) {
14+
private constructor(builder: Builder) : this(
15+
builder.searchTerm,
16+
builder.originalQuery,
17+
builder.resultGroup,
18+
builder.analyticsTags,
19+
)
20+
21+
companion object {
22+
inline fun build(
23+
searchTerm: String,
24+
originalQuery: String,
25+
block: Builder.() -> Unit = {}
26+
) = Builder(searchTerm, originalQuery).apply(block).build()
27+
}
28+
29+
class Builder(
30+
val searchTerm: String,
31+
val originalQuery: String
32+
) {
33+
var resultGroup: ResultGroup? = null
34+
var analyticsTags: Map<String, String>? = null
35+
36+
fun setResultGroup(resultGroup: ResultGroup): Builder = apply { this.resultGroup = resultGroup }
37+
fun setAnalyticsTags(analyticsTags: Map<String, String>): Builder = apply { this.analyticsTags = analyticsTags }
38+
fun build(): SearchSubmitTrackingData = SearchSubmitTrackingData(this)
39+
}
40+
}

library/src/test/java/io/constructor/core/ConstructorIoTrackingTest.kt

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import io.constructor.data.memory.ConfigMemoryHolder
66
import io.constructor.data.model.common.ResultGroup
77
import io.constructor.data.model.purchase.PurchaseItem
88
import io.constructor.data.model.common.TrackingItem
9+
import io.constructor.data.builder.SearchSubmitTrackingData
910
import io.constructor.test.createTestDataManager
1011
import io.constructor.util.RxSchedulersOverrideRule
1112
import io.mockk.every
@@ -340,18 +341,43 @@ class ConstructorIoTrackingTest {
340341
val observer = ConstructorIo.trackSearchSubmitInternal("titanic", "tit", null).test()
341342
observer.assertComplete()
342343
val request = mockServer.takeRequest()
343-
val path = "/autocomplete/titanic/search?original_query=tit&tr=search&key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.44.0&_dt="
344+
val path = "/autocomplete/titanic/search?original_query=tit&tr=search&analytics_tags%5BappVersion%5D=123&analytics_tags%5BappPlatform%5D=Android&key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.44.0&_dt="
344345
assert(request.path!!.startsWith(path))
345346
}
346347

348+
@Test
349+
fun trackSearchSubmitWithAnalyticsTags() {
350+
val mockResponse = MockResponse().setResponseCode(204)
351+
mockServer.enqueue(mockResponse)
352+
val observer = ConstructorIo.trackSearchSubmitInternal("titanic", "tit", null, mapOf("test" to "test1", "appVersion" to "150")).test()
353+
observer.assertComplete()
354+
val request = mockServer.takeRequest()
355+
val path = "/autocomplete/titanic/search?original_query=tit&tr=search&analytics_tags%5BappVersion%5D=150&analytics_tags%5BappPlatform%5D=Android&analytics_tags%5Btest%5D=test1&key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.44.0&_dt="
356+
assert(request.path!!.startsWith(path))
357+
}
358+
359+
@Test
360+
fun trackSearchSubmitWithRequestBuilder() {
361+
val mockResponse = MockResponse().setResponseCode(204)
362+
mockServer.enqueue(mockResponse)
363+
val request = SearchSubmitTrackingData.build("titanic", "tit") {
364+
setResultGroup(ResultGroup("Movies", "group_id"))
365+
setAnalyticsTags(mapOf("relatedSearchTerm" to "true"))
366+
}
367+
ConstructorIo.trackSearchSubmit(request)
368+
val recordedRequest = mockServer.takeRequest()
369+
val path = "/autocomplete/titanic/search?original_query=tit&tr=search&group%5Bgroup_id%5D=group_id&group%5Bdisplay_name%5D=Movies&analytics_tags%5BappVersion%5D=123&analytics_tags%5BappPlatform%5D=Android&analytics_tags%5BrelatedSearchTerm%5D=true&key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.44.0&_dt="
370+
assert(recordedRequest.path!!.startsWith(path))
371+
}
372+
347373
@Test
348374
fun trackSearchSubmit500() {
349375
val mockResponse = MockResponse().setResponseCode(500).setBody("Internal server error")
350376
mockServer.enqueue(mockResponse)
351377
val observer = ConstructorIo.trackSearchSubmitInternal("titanic", "tit", null).test()
352378
observer.assertError { true }
353379
val request = mockServer.takeRequest()
354-
val path = "/autocomplete/titanic/search?original_query=tit&tr=search&key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.44.0&_dt="
380+
val path = "/autocomplete/titanic/search?original_query=tit&tr=search&analytics_tags%5BappVersion%5D=123&analytics_tags%5BappPlatform%5D=Android&key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.44.0&_dt="
355381
assert(request.path!!.startsWith(path))
356382
}
357383

@@ -363,7 +389,7 @@ class ConstructorIoTrackingTest {
363389
val observer = ConstructorIo.trackSearchSubmitInternal("titanic", "tit", null).test()
364390
observer.assertError(SocketTimeoutException::class.java)
365391
val request = mockServer.takeRequest()
366-
val path = "/autocomplete/titanic/search?original_query=tit&tr=search&key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.44.0&_dt="
392+
val path = "/autocomplete/titanic/search?original_query=tit&tr=search&analytics_tags%5BappVersion%5D=123&analytics_tags%5BappPlatform%5D=Android&key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.44.0&_dt="
367393
assert(request.path!!.startsWith(path))
368394
}
369395

library/src/test/java/io/constructor/core/ConstructorioSegmentsTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class ConstructorioSegmentsTest {
109109
val observer = ConstructorIo.trackSearchSubmitInternal("titanic", "tit", null).test()
110110
observer.assertComplete()
111111
val request = mockServer.takeRequest()
112-
val path = "/autocomplete/titanic/search?original_query=tit&tr=search&key=aluminium-key&i=koopa-the-guid&ui=player-two&s=14&us=mobile&us=COUNTRY_US&c=cioand-2.44.0&_dt="
112+
val path = "/autocomplete/titanic/search?original_query=tit&tr=search&analytics_tags%5BappVersion%5D=123&analytics_tags%5BappPlatform%5D=Android&key=aluminium-key&i=koopa-the-guid&ui=player-two&s=14&us=mobile&us=COUNTRY_US&c=cioand-2.44.0&_dt="
113113
assert(request.path!!.startsWith(path))
114114
}
115115

library/src/test/java/io/constructor/core/ConstructorioTestCellTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ class ConstructorioTestCellTest {
110110
val observer = ConstructorIo.trackSearchSubmitInternal("titanic", "tit", null).test()
111111
observer.assertComplete()
112112
val request = mockServer.takeRequest()
113-
val path = "/autocomplete/titanic/search?original_query=tit&tr=search&key=aluminium-key&i=koopa-the-guid&ui=player-two&s=14&ef-cellone=vanilla&ef-celltwo=whipped-cream&c=cioand-2.44.0&_dt=";
113+
val path = "/autocomplete/titanic/search?original_query=tit&tr=search&analytics_tags%5BappVersion%5D=123&analytics_tags%5BappPlatform%5D=Android&key=aluminium-key&i=koopa-the-guid&ui=player-two&s=14&ef-cellone=vanilla&ef-celltwo=whipped-cream&c=cioand-2.44.0&_dt=";
114114
assert(request.path!!.startsWith(path))
115115
}
116116

0 commit comments

Comments
 (0)