Skip to content

Commit de14e88

Browse files
authored
[CSL 7] Convert Conversion to v2 Endpoint (#63)
* Convert conversion to v2 * Update tests * Remove unused imports
1 parent 39bda12 commit de14e88

File tree

9 files changed

+94
-27
lines changed

9 files changed

+94
-27
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,8 @@ ConstructorIo.trackBrowseResultClick("Category", "Snacks", "7654321-BA", "4", "P
131131
### Conversion Events
132132

133133
```kotlin
134-
// // Track when an item converts (a.k.a. is added to cart) regardless of the user journey that led to adding to cart (itemName, customerId, revenue, searchTerm)
135-
ConstructorIo.trackConversion("Fashionable Toothpicks", "1234567-AB", 12.99, "tooth")
134+
// Track when an item converts (a.k.a. is added to cart) regardless of the user journey that led to adding to cart (itemName, customerId, revenue, searchTerm, section, conversionType)
135+
ConstructorIo.trackConversion("Fashionable Toothpicks", "1234567-AB", 12.99, "tooth", "Products", "add_to_cart")
136136

137137
// Track when items are purchased (customerIds, revenue, orderId)
138138
ConstructorIo.trackPurchase(arrayOf("1234567-AB", "1234567-AB"), 25.98, "ORD-1312343")

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

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import io.constructor.data.model.browse.BrowseResultClickRequestBody
1515
import io.constructor.data.model.browse.BrowseResultLoadRequestBody
1616
import io.constructor.data.model.purchase.PurchaseItem
1717
import io.constructor.data.model.purchase.PurchaseRequestBody
18+
import io.constructor.data.model.conversion.ConversionRequestBody
1819
import io.constructor.injection.component.AppComponent
1920
import io.constructor.injection.component.DaggerAppComponent
2021
import io.constructor.injection.module.AppModule
@@ -313,18 +314,33 @@ object ConstructorIo {
313314
* @param searchTerm the search term that lead to the event (if adding to cart in a search flow)
314315
* @param sectionName the section that the results came from, i.e. "Products"
315316
*/
316-
fun trackConversion(itemName: String, customerId: String, revenue: Double?, searchTerm: String = Constants.QueryConstants.TERM_UNKNOWN, sectionName: String? = null) {
317-
var completable = trackConversionInternal(itemName, customerId, revenue, searchTerm, sectionName)
317+
fun trackConversion(itemName: String, customerId: String, revenue: Double?, searchTerm: String = Constants.QueryConstants.TERM_UNKNOWN, sectionName: String? = null, conversionType: String? = null) {
318+
var completable = trackConversionInternal(itemName, customerId, revenue, searchTerm, sectionName, conversionType)
318319
disposable.add(completable.subscribeOn(Schedulers.io()).subscribe({}, {
319320
t -> e("Conversion error: ${t.message}")
320321
}))
321322
}
322-
internal fun trackConversionInternal(itemName: String, customerId: String, revenue: Double?, searchTerm: String = Constants.QueryConstants.TERM_UNKNOWN, sectionName: String? = null): Completable {
323+
internal fun trackConversionInternal(itemName: String, customerId: String, revenue: Double?, searchTerm: String = Constants.QueryConstants.TERM_UNKNOWN, sectionName: String? = null, conversionType: String? = null): Completable {
323324
preferenceHelper.getSessionId(sessionIncrementHandler)
324-
val revenueString = revenue?.let { "%.2f".format(revenue) }
325-
return dataManager.trackConversion(searchTerm, itemName, customerId, revenueString, arrayOf(
326-
Constants.QueryConstants.SECTION to (sectionName ?: preferenceHelper.defaultItemSection)
327-
))
325+
val section = sectionName ?: preferenceHelper.defaultItemSection
326+
val conversionRequestBody = ConversionRequestBody(
327+
searchTerm,
328+
customerId,
329+
itemName,
330+
String.format("%.2f", revenue),
331+
conversionType,
332+
BuildConfig.CLIENT_VERSION,
333+
preferenceHelper.id,
334+
preferenceHelper.getSessionId(),
335+
preferenceHelper.apiKey,
336+
configMemoryHolder.userId,
337+
configMemoryHolder.segments,
338+
true,
339+
section,
340+
System.currentTimeMillis()
341+
)
342+
343+
return dataManager.trackConversion(conversionRequestBody, arrayOf())
328344
}
329345

330346
/**
@@ -394,7 +410,7 @@ object ConstructorIo {
394410

395411
return dataManager.trackBrowseResultsLoaded(
396412
browseResultLoadRequestBody,
397-
arrayOf(Constants.QueryConstants.SECTION to section)
413+
arrayOf()
398414
)
399415
}
400416

library/src/main/java/io/constructor/data/DataManager.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import io.constructor.data.model.search.SearchResponse
66
import io.constructor.data.model.browse.BrowseResponse
77
import io.constructor.data.model.browse.BrowseResultClickRequestBody
88
import io.constructor.data.model.browse.BrowseResultLoadRequestBody
9+
import io.constructor.data.model.conversion.ConversionRequestBody
910
import io.constructor.data.model.purchase.PurchaseRequestBody
1011
import io.constructor.data.remote.ApiPaths
1112
import io.constructor.data.remote.ConstructorApi
@@ -74,8 +75,8 @@ constructor(private val constructorApi: ConstructorApi, private val moshi: Moshi
7475
return constructorApi.trackSessionStart(params.toMap())
7576
}
7677

77-
fun trackConversion(term: String, itemName: String, customerId: String, revenue: String? = null, params: Array<Pair<String, String>> = arrayOf()): Completable {
78-
return constructorApi.trackConversion(term, itemName, customerId, revenue, params.toMap())
78+
fun trackConversion(conversionRequestBody: ConversionRequestBody, params: Array<Pair<String, String>> = arrayOf()): Completable {
79+
return constructorApi.trackConversion(conversionRequestBody, params.toMap())
7980
}
8081

8182
fun trackSearchResultClick(itemName: String, customerId: String, term: String, params: Array<Pair<String, String>> = arrayOf(), encodedParams: Array<Pair<String, String>> = arrayOf()): Completable {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package io.constructor.data.model.conversion
2+
3+
import com.squareup.moshi.Json
4+
import io.constructor.data.model.common.*;
5+
import java.io.Serializable
6+
7+
data class ConversionRequestBody(
8+
@Json(name = "search_term") val searchTerm: String,
9+
@Json(name = "item_id") val itemID: String,
10+
@Json(name = "item_name") val itemName: String,
11+
@Json(name = "revenue") val revenue: String,
12+
@Json(name = "conversion_type") val conversionType: String?,
13+
@Json(name = "c") val c: String,
14+
@Json(name = "i") val i: String,
15+
@Json(name = "s") val s: Int,
16+
@Json(name = "key") val key: String,
17+
@Json(name = "ui") val ui: String?,
18+
@Json(name = "us") val us: List<String?>,
19+
@Json(name= "beacon") val beacon: Boolean?,
20+
@Json(name= "section") val section: String?,
21+
@Json(name= "_dt") val _dt: Long?
22+
) : Serializable

library/src/main/java/io/constructor/data/remote/ApiPaths.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ object ApiPaths {
55
const val URL_AUTOCOMPLETE_SELECT_EVENT = "autocomplete/{term}/select"
66
const val URL_SEARCH_SUBMIT_EVENT = "autocomplete/{term}/search"
77
const val URL_SESSION_START_EVENT = "behavior"
8-
const val URL_CONVERSION_EVENT = "autocomplete/{term}/conversion"
8+
const val URL_CONVERSION_EVENT = "v2/behavioral_action/conversion"
99
const val URL_SEARCH_RESULT_CLICK_EVENT = "autocomplete/{term}/click_through"
1010
const val URL_BEHAVIOR = "behavior"
1111
const val URL_PURCHASE = "v2/behavioral_action/purchase"

library/src/main/java/io/constructor/data/remote/ConstructorApi.kt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package io.constructor.data.remote
33
import io.constructor.data.model.browse.BrowseResultClickRequestBody
44
import io.constructor.data.model.browse.BrowseResultLoadRequestBody
55
import io.constructor.data.model.purchase.PurchaseRequestBody
6+
import io.constructor.data.model.conversion.ConversionRequestBody
67
import io.reactivex.Completable
78
import io.reactivex.Single
89
import okhttp3.ResponseBody
@@ -28,11 +29,8 @@ interface ConstructorApi {
2829
@GET(ApiPaths.URL_SESSION_START_EVENT)
2930
fun trackSessionStart(@QueryMap params: Map<String, String>): Completable
3031

31-
@GET(ApiPaths.URL_CONVERSION_EVENT)
32-
fun trackConversion(@Path("term") term: String,
33-
@Query("name") itemName: String,
34-
@Query("customer_id") customerId: String,
35-
@Query("revenue") revenue: String?,
32+
@POST(ApiPaths.URL_CONVERSION_EVENT)
33+
fun trackConversion(@Body conversionRequestBody: ConversionRequestBody,
3634
@QueryMap params: Map<String, String>): Completable
3735

3836
@GET(ApiPaths.URL_SEARCH_RESULT_CLICK_EVENT)

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

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import io.mockk.every
1010
import io.mockk.mockk
1111
import okhttp3.mockwebserver.MockResponse
1212
import okhttp3.mockwebserver.MockWebServer
13+
import okhttp3.mockwebserver.RecordedRequest
1314
import org.junit.Before
1415
import org.junit.Rule
1516
import org.junit.Test
@@ -18,6 +19,15 @@ import java.util.concurrent.TimeUnit
1819
import kotlin.test.assertEquals
1920
import kotlin.test.assertTrue
2021

22+
internal fun getRequestBody(request: RecordedRequest): Map<String, String> {
23+
val requestBodyString = request.body.readUtf8().drop(1).dropLast(1).replace("\"", "")
24+
val requestBodyList = ArrayList(requestBodyString.split(","))
25+
return requestBodyList.associate {
26+
val (key, value) = it.split(":")
27+
key to value
28+
}
29+
}
30+
2131
class ConstructorIoTrackingTest {
2232

2333
@Rule
@@ -299,7 +309,26 @@ class ConstructorIoTrackingTest {
299309
val observer = ConstructorIo.trackConversionInternal("titanic replica", "TIT-REP-1997", 89.00).test()
300310
observer.assertComplete()
301311
val request = mockServer.takeRequest()
302-
val path = "/autocomplete/TERM_UNKNOWN/conversion?name=titanic%20replica&customer_id=TIT-REP-1997&revenue=89.00&section=Products&key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.5.2&_dt=";
312+
val path = "/v2/behavioral_action/conversion?key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.5.2&_dt=";
313+
assert(request.path.startsWith(path))
314+
assertTrue(request.bodySize > 220)
315+
assertEquals("POST", request.method)
316+
}
317+
318+
@Test
319+
fun trackConversionWithConversionType() {
320+
val mockResponse = MockResponse().setResponseCode(204)
321+
mockServer.enqueue(mockResponse)
322+
val observer = ConstructorIo.trackConversionInternal("titanic replica", "TIT-REP-1997", 89.00, "titanic", "Products", "Like").test()
323+
observer.assertComplete()
324+
val request = mockServer.takeRequest()
325+
val requestBody = getRequestBody(request)
326+
val path = "/v2/behavioral_action/conversion?key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.5.2&_dt=";
327+
assertEquals(requestBody["conversion_type"], "Like")
328+
assertEquals(requestBody["revenue"], "89.00")
329+
assertEquals(requestBody["search_term"], "titanic")
330+
assertEquals(requestBody["section"], "Products")
331+
assertEquals(request.method,"POST")
303332
assert(request.path.startsWith(path))
304333
}
305334

@@ -310,8 +339,10 @@ class ConstructorIoTrackingTest {
310339
val observer = ConstructorIo.trackConversionInternal("titanic replica", "TIT-REP-1997", 89.00).test()
311340
observer.assertError { true }
312341
val request = mockServer.takeRequest()
313-
val path = "/autocomplete/TERM_UNKNOWN/conversion?name=titanic%20replica&customer_id=TIT-REP-1997&revenue=89.00&section=Products&key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.5.2&_dt=";
342+
val path = "/v2/behavioral_action/conversion?key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.5.2&_dt=";
314343
assert(request.path.startsWith(path))
344+
assertTrue(request.bodySize > 220)
345+
assertEquals("POST", request.method)
315346
}
316347

317348
@Test
@@ -321,9 +352,8 @@ class ConstructorIoTrackingTest {
321352
mockServer.enqueue(mockResponse)
322353
val observer = ConstructorIo.trackConversionInternal("titanic replica", "TIT-REP-1997", 89.00).test()
323354
observer.assertError(SocketTimeoutException::class.java)
324-
val request = mockServer.takeRequest()
325-
val path = "/autocomplete/TERM_UNKNOWN/conversion?name=titanic%20replica&customer_id=TIT-REP-1997&revenue=89.00&section=Products&key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.5.2&_dt=";
326-
assert(request.path.startsWith(path))
355+
val request = mockServer.takeRequest(10, TimeUnit.SECONDS)
356+
assertEquals(null, request)
327357
}
328358

329359
@Test
@@ -380,7 +410,7 @@ class ConstructorIoTrackingTest {
380410
val observer = ConstructorIo.trackBrowseResultsLoadedInternal("group_id", "Movies", 10).test()
381411
observer.assertComplete()
382412
val request = mockServer.takeRequest()
383-
val path = "/v2/behavioral_action/browse_result_load?section=Products&key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.5.2&_dt="
413+
val path = "/v2/behavioral_action/browse_result_load?key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.5.2&_dt="
384414
assert(request.path.startsWith(path))
385415
assertTrue(request.bodySize > 215)
386416
assertEquals("POST", request.method)
@@ -393,7 +423,7 @@ class ConstructorIoTrackingTest {
393423
val observer = ConstructorIo.trackBrowseResultsLoadedInternal("group_id", "Movies", 10).test()
394424
observer.assertError { true }
395425
val request = mockServer.takeRequest()
396-
val path = "/v2/behavioral_action/browse_result_load?section=Products&key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.5.2&_dt="
426+
val path = "/v2/behavioral_action/browse_result_load?key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.5.2&_dt="
397427
assert(request.path.startsWith(path))
398428
assertTrue(request.bodySize > 220)
399429
assertEquals("POST", request.method)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ class ConstructorioSegmentsTest {
140140
val observer = ConstructorIo.trackConversionInternal("titanic replica", "TIT-REP-1997", 89.00).test()
141141
observer.assertComplete()
142142
val request = mockServer.takeRequest()
143-
val path = "/autocomplete/TERM_UNKNOWN/conversion?name=titanic%20replica&customer_id=TIT-REP-1997&revenue=89.00&section=Products&key=aluminium-key&i=koopa-the-guid&ui=player-two&s=14&us=mobile&us=COUNTRY_US&c=cioand-2.5.2&_dt=";
143+
val path = "/v2/behavioral_action/conversion?key=aluminium-key&i=koopa-the-guid&ui=player-two&s=14&us=mobile&us=COUNTRY_US&c=cioand-2.5.2&_dt=";
144144
assert(request.path.startsWith(path))
145145
}
146146

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ class ConstructorioTestCellTest {
141141
val observer = ConstructorIo.trackConversionInternal("titanic replica", "TIT-REP-1997", 89.00).test()
142142
observer.assertComplete()
143143
val request = mockServer.takeRequest()
144-
val path = "/autocomplete/TERM_UNKNOWN/conversion?name=titanic%20replica&customer_id=TIT-REP-1997&revenue=89.00&section=Products&key=aluminium-key&i=koopa-the-guid&ui=player-two&s=14&ef-cellone=vanilla&ef-celltwo=whipped-cream&c=cioand-2.5.2&_dt=";
144+
val path = "/v2/behavioral_action/conversion?key=aluminium-key&i=koopa-the-guid&ui=player-two&s=14&ef-cellone=vanilla&ef-celltwo=whipped-cream&c=cioand-2.5.2&_dt=";
145145
assert(request.path.startsWith(path))
146146
}
147147

0 commit comments

Comments
 (0)