Skip to content

Commit 6166dbd

Browse files
authored
[CH11983] Browse Support (#51)
* add getBrowseResults * Add trackBrowseResultsClick and trackBrowseResultsLoaded * Update readme
1 parent 02d7c41 commit 6166dbd

File tree

15 files changed

+1652
-1
lines changed

15 files changed

+1652
-1
lines changed

README.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,22 @@ ConstructorIo.getSearchResults(query, selectedFacets?.map { it.key to it.value }
6565
## 6. Request Browse Events
6666

6767
```kotlin
68-
// Coming end of October
68+
var page = 1
69+
var perPage = 10
70+
var filterName = "group_id"
71+
var filterValue = "Beverages"
72+
var selectedFacets: HashMap<String, MutableList<String>>? = null
73+
var selectedSortOption: SortOption? = null
74+
75+
ConstructorIo.getBrowseResults(filterName, filterValue, selectedFacets?.map { it.key to it.value }, page = page, perPage = limit, sortBy = selectedSortOption?.sortBy, sortOrder = selectedSortOption?.sortOrder)
76+
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
77+
.subscribe {
78+
it.onValue {
79+
it.browseData?.let {
80+
view.renderData(it)
81+
}
82+
}
83+
}
6984
```
7085

7186
## 7. Instrument Behavioral Events
@@ -106,3 +121,13 @@ ConstructorIo.trackConversion("Fashionable Toothpicks", "1234567-AB", 12.99, "to
106121
// Track when products are purchased (customerIds, revenue, orderId)
107122
ConstructorIo.trackPurchase(arrayOf("1234567-AB", "1234567-AB"), 25.98, "ORD-1312343")
108123
```
124+
125+
### Browse Events
126+
127+
```kotlin
128+
// Track when browse results are loaded into view (filterName, filterValue, resultCount)
129+
ConstructorIo.trackBrowseResultsLoaded("Category", "Snacks", 674)
130+
131+
// Track when a browse result is clicked (filterName, filterValue, customerId, resultPositionOnPage)
132+
ConstructorIo.trackBrowseResultClick("Category", "Snacks", "7654321-BA", "4")
133+
```

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class Constants {
4343
const val SEARCH_SUGGESTIONS = "Search Suggestions"
4444
const val PRODUCTS = "Products"
4545
const val EVENT_SEARCH_RESULTS = "search-results"
46+
const val EVENT_BROWSE_RESULTS = "browse-results"
4647
const val EVENT_INPUT_FOCUS = "focus"
4748
}
4849
}

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

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import io.constructor.data.memory.ConfigMemoryHolder
99
import io.constructor.data.model.Group
1010
import io.constructor.data.model.Suggestion
1111
import io.constructor.data.model.search.SearchResponse
12+
import io.constructor.data.model.browse.BrowseResponse
1213
import io.constructor.injection.component.AppComponent
1314
import io.constructor.injection.component.DaggerAppComponent
1415
import io.constructor.injection.module.AppModule
@@ -267,4 +268,59 @@ object ConstructorIo {
267268
val params = mutableListOf(Constants.QueryConstants.AUTOCOMPLETE_SECTION to sectionNameParam)
268269
return dataManager.trackPurchase(clientIds.toList(), revenueString, orderID, params.toTypedArray())
269270
}
271+
272+
/**
273+
* Returns browse results including filters, categories, sort options, etc.
274+
*/
275+
fun getBrowseResults(filterName: String, filterValue: String, facets: List<Pair<String, List<String>>>? = null, page: Int? = null, perPage: Int? = null, groupId: Int? = null, sortBy: String? = null, sortOrder: String? = null): Observable<ConstructorData<BrowseResponse>> {
276+
val encodedParams: ArrayList<Pair<String, String>> = arrayListOf()
277+
groupId?.let { encodedParams.add(Constants.QueryConstants.FILTER_GROUP_ID.urlEncode() to it.toString()) }
278+
page?.let { encodedParams.add(Constants.QueryConstants.PAGE.urlEncode() to page.toString().urlEncode()) }
279+
perPage?.let { encodedParams.add(Constants.QueryConstants.PER_PAGE.urlEncode() to perPage.toString().urlEncode()) }
280+
sortBy?.let { encodedParams.add(Constants.QueryConstants.SORT_BY.urlEncode() to it.urlEncode()) }
281+
sortOrder?.let { encodedParams.add(Constants.QueryConstants.SORT_ORDER.urlEncode() to it.urlEncode()) }
282+
facets?.forEach { facet ->
283+
facet.second.forEach {
284+
encodedParams.add(Constants.QueryConstants.FILTER_FACET.format(facet.first).urlEncode() to it.urlEncode())
285+
}
286+
}
287+
return dataManager.getBrowseResults(filterName, filterValue, encodedParams = encodedParams.toTypedArray())
288+
}
289+
290+
/**
291+
* Tracks browse results loaded (a.k.a. browse results viewed) events
292+
*/
293+
fun trackBrowseResultsLoaded(filterName: String, filterValue: String, resultCount: Int) {
294+
var completable = trackBrowseResultsLoadedInternal(filterName, filterValue, resultCount)
295+
disposable.add(completable.subscribeOn(Schedulers.io()).subscribe({}, {
296+
t -> e("Browse Results Loaded error: ${t.message}")
297+
}))
298+
}
299+
internal fun trackBrowseResultsLoadedInternal(filterName: String, filterValue: String, resultCount: Int): Completable {
300+
preferenceHelper.getSessionId(sessionIncrementHandler)
301+
return dataManager.trackBrowseResultsLoaded(filterName, filterValue, resultCount, arrayOf(
302+
Constants.QueryConstants.ACTION to Constants.QueryValues.EVENT_BROWSE_RESULTS
303+
))
304+
}
305+
306+
/**
307+
* Tracks browse result click events
308+
*/
309+
fun trackBrowseResultClick(filterName: String, filterValue: String, customerId: String, resultPositionOnPage: Int, sectionName: String? = null, resultID: String? = null) {
310+
var completable = trackBrowseResultClickInternal(filterName, filterValue, customerId, resultPositionOnPage, sectionName, resultID)
311+
disposable.add(completable.subscribeOn(Schedulers.io()).subscribe({}, {
312+
t -> e("Browse Result Click error: ${t.message}")
313+
}))
314+
}
315+
internal fun trackBrowseResultClickInternal(filterName: String, filterValue: String, customerId: String, resultPositionOnPage: Int, sectionName: String? = null, resultID: String? = null): Completable {
316+
preferenceHelper.getSessionId(sessionIncrementHandler)
317+
val encodedParams: ArrayList<Pair<String, String>> = arrayListOf()
318+
resultID?.let { encodedParams.add(Constants.QueryConstants.RESULT_ID.urlEncode() to it.urlEncode()) }
319+
val sName = sectionName ?: preferenceHelper.defaultItemSection
320+
return dataManager.trackBrowseResultClick(filterName, filterValue, customerId, resultPositionOnPage, arrayOf(
321+
Constants.QueryConstants.AUTOCOMPLETE_SECTION to sName
322+
), encodedParams.toTypedArray())
323+
324+
}
325+
270326
}

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package io.constructor.data
33
import com.squareup.moshi.Moshi
44
import io.constructor.data.model.Suggestion
55
import io.constructor.data.model.search.SearchResponse
6+
import io.constructor.data.model.browse.BrowseResponse
67
import io.constructor.data.remote.ApiPaths
78
import io.constructor.data.remote.ConstructorApi
89
import io.reactivex.Completable
@@ -84,4 +85,36 @@ constructor(private val constructorApi: ConstructorApi, private val moshi: Moshi
8485
return constructorApi.trackPurchase(customerIds, revenue, orderID, params.toMap())
8586
}
8687

88+
fun getBrowseResults(filterName: String, filterValue: String, encodedParams: Array<Pair<String, String>> = arrayOf()): Observable<ConstructorData<BrowseResponse>> {
89+
var dynamicUrl = "/${ApiPaths.URL_BROWSE.format(filterName, filterValue)}"
90+
encodedParams.forEachIndexed { index, pair ->
91+
dynamicUrl += "${if (index != 0) "&" else "?" }${pair.first}=${pair.second}"
92+
}
93+
return constructorApi.getBrowseResults(dynamicUrl).map { result ->
94+
if (!result.isError) {
95+
result.response()?.let {
96+
if (it.isSuccessful){
97+
val adapter = moshi.adapter(BrowseResponse::class.java)
98+
val response = it.body()?.string()
99+
val result = response?.let { adapter.fromJson(it) }
100+
result?.rawData = response
101+
ConstructorData.of(result!!)
102+
} else {
103+
ConstructorData.networkError(it.errorBody()?.string())
104+
}
105+
} ?: ConstructorData.error(result.error())
106+
} else {
107+
ConstructorData.error(result.error())
108+
}
109+
}.toObservable()
110+
}
111+
112+
fun trackBrowseResultClick(filterName: String, filterValue: String, customerId: String, resultPositionOnPage: Int, params: Array<Pair<String, String>> = arrayOf(), encodedParams: Array<Pair<String, String>> = arrayOf()): Completable {
113+
return constructorApi.trackBrowseResultClick(filterName, filterValue, customerId, resultPositionOnPage, params.toMap(), encodedParams.toMap())
114+
}
115+
116+
fun trackBrowseResultsLoaded(filterName: String, filterValue: String, resultCount: Int, params: Array<Pair<String, String>>): Completable {
117+
return constructorApi.trackBrowseResultsLoaded(filterName, filterValue, resultCount, params.toMap())
118+
}
119+
87120
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package io.constructor.data.model.browse
2+
3+
import com.squareup.moshi.Json
4+
import io.constructor.data.model.search.SortOption
5+
6+
data class BrowseData(val facets: List<BrowseFacet>?, val groups: List<BrowseGroup>?, @Json(name = "results") val browseResults: List<BrowseResult>?, @Json(name = "sort_options") val sortOptions: List<SortOption>? = null, @Json(name = "total_num_results") val resultCount: Int)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package io.constructor.data.model.browse
2+
3+
import com.squareup.moshi.Json
4+
import java.io.Serializable
5+
import io.constructor.data.model.search.FacetOption
6+
7+
8+
data class BrowseFacet(val name: String,
9+
@Json(name = "display_name") val displayName: String?,
10+
val status: Map<String, Any>?,
11+
val type: String?,
12+
val min: Double?,
13+
val max: Double?,
14+
val options: List<FacetOption>?) : Serializable
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package io.constructor.data.model.browse
2+
3+
import com.squareup.moshi.Json
4+
5+
data class BrowseGroup(@Json(name = "children") val children: List<BrowseGroup>?,
6+
@Json(name = "parents") val parents: List<BrowseGroup>?,
7+
val count: Int?,
8+
@Json(name = "display_name") val displayName: String,
9+
@Json(name = "group_id") val groupId: String)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package io.constructor.data.model.browse
2+
3+
import com.squareup.moshi.Json
4+
5+
data class BrowseResponse(@Json(name = "response") val browseData: BrowseData, @Json(name = "result_id") val resultId: String, var rawData: String?)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package io.constructor.data.model.browse
2+
3+
import com.squareup.moshi.Json
4+
import io.constructor.data.model.search.ResultData
5+
import java.io.Serializable
6+
7+
data class BrowseResult(@Json(name = "data") val result: ResultData, @Json(name = "matched_terms") val matchedTerms: List<String>?, val value: String) : Serializable

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,7 @@ object ApiPaths {
1010
const val URL_BEHAVIOR = "behavior"
1111
const val URL_PURCHASE = "autocomplete/TERM_UNKNOWN/purchase"
1212
const val URL_SEARCH = "search/%s"
13+
const val URL_BROWSE = "browse/%s/%s"
14+
const val URL_BROWSE_RESULT_CLICK_EVENT = "v2/behavioral_action/browse_result_click"
15+
const val URL_BROWSE_RESULT_LOAD_EVENT = "v2/behavioral_action/browse_result_load"
1316
}

0 commit comments

Comments
 (0)