Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 95 additions & 107 deletions library/src/main/java/io/constructor/core/ConstructorIo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,9 @@ import io.constructor.injection.component.DaggerAppComponent
import io.constructor.injection.module.AppModule
import io.constructor.injection.module.NetworkModule
import io.constructor.util.broadcastIntent
import io.constructor.util.d
import io.constructor.util.e
import io.constructor.util.urlEncode
import io.reactivex.Completable
import io.reactivex.Observable
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import java.util.*

Expand All @@ -31,7 +29,7 @@ object ConstructorIo {
private lateinit var preferenceHelper: PreferencesHelper
private lateinit var configMemoryHolder: ConfigMemoryHolder
private lateinit var context: Context
private var disposable = CompositeDisposable()
private var broadcast = true

var userId: String?
get() = configMemoryHolder.userId
Expand All @@ -46,16 +44,8 @@ object ConstructorIo {
.build()
}

private var sessionIncrementEventHandler: (String) -> Unit = {
trackSessionStartInternal(it)
}

private fun trackSessionStartInternal(sessionId: String, errorCallback: ConstructorError = null) {
disposable.add(dataManager.trackSessionStart(arrayOf(Constants.QueryConstants.SESSION to sessionId,
Constants.QueryConstants.ACTION to Constants.QueryValues.EVENT_SESSION_START)).subscribeOn(Schedulers.io()).subscribe({}, {
errorCallback?.invoke(it)
d("Error triggering Session Change event")
}))
private var sessionIncrementHandler: (String) -> Unit = {
trackSessionStartInternal()
}

fun init(context: Context?, constructorIoConfig: ConstructorIoConfig) {
Expand Down Expand Up @@ -88,14 +78,11 @@ object ConstructorIo {
this.dataManager = dataManager
this.preferenceHelper = preferenceHelper
this.configMemoryHolder = configMemoryHolder
preferenceHelper.token = constructorIoConfig.apiKey
if (preferenceHelper.id.isBlank()) {
preferenceHelper.id = UUID.randomUUID().toString()
}
this.broadcast = false
}

fun appMovedToForeground() {
preferenceHelper.getSessionId(sessionIncrementEventHandler)
preferenceHelper.getSessionId(sessionIncrementHandler)
}

fun getAutocompleteResults(query: String): Observable<ConstructorData<List<Suggestion>?>> {
Expand All @@ -107,16 +94,11 @@ object ConstructorIo {
}

fun getSearchResults(text: String, vararg facets: Pair<String, List<String>>, page: Int? = null, perPage: Int? = null, groupId: Int? = null): Observable<ConstructorData<SearchResponse>> {
val sessionId = preferenceHelper.getSessionId(sessionIncrementEventHandler)
preferenceHelper.getSessionId(sessionIncrementHandler)
val encodedParams: ArrayList<Pair<String, String>> = arrayListOf()
groupId?.let { encodedParams.add(Constants.QueryConstants.FILTER_GROUP_ID.urlEncode() to it.toString()) }
page?.let {
encodedParams.add(Constants.QueryConstants.PAGE.urlEncode() to page.toString().urlEncode())
}
perPage?.let {
encodedParams.add(Constants.QueryConstants.PER_PAGE.urlEncode() to perPage.toString().urlEncode())
}
encodedParams.add(Constants.QueryConstants.SESSION.urlEncode() to sessionId.toString().urlEncode())
page?.let { encodedParams.add(Constants.QueryConstants.PAGE.urlEncode() to page.toString().urlEncode()) }
perPage?.let { encodedParams.add(Constants.QueryConstants.PER_PAGE.urlEncode() to perPage.toString().urlEncode()) }
facets.forEach { facet ->
facet.second.forEach {
encodedParams.add(Constants.QueryConstants.FILTER_FACET.format(facet.first).urlEncode() to it.urlEncode())
Expand All @@ -125,106 +107,112 @@ object ConstructorIo {
return dataManager.getSearchResults(text, encodedParams = encodedParams.toTypedArray())
}

fun trackAutocompleteSelect(searchTerm: String, originalQuery: String, sectionName: String, group: Group? = null, errorCallback: ConstructorError = null) {
val sessionId = preferenceHelper.getSessionId(sessionIncrementEventHandler)
/**
* Tracks Session Start Events
*/
internal fun trackSessionStartInternal (): Completable {
return dataManager.trackSessionStart(
arrayOf(Constants.QueryConstants.ACTION to Constants.QueryValues.EVENT_SESSION_START)
)
}

/**
* Tracks input focus events
*/
fun trackInputFocus(term: String?): Completable {
preferenceHelper.getSessionId(sessionIncrementHandler)
return dataManager.trackInputFocus(term, arrayOf(
Constants.QueryConstants.ACTION to Constants.QueryValues.EVENT_INPUT_FOCUS
));
}

/**
* Tracks autocomplete select events
*/
fun trackAutocompleteSelect(searchTerm: String, originalQuery: String, sectionName: String, group: Group? = null): Completable {
preferenceHelper.getSessionId(sessionIncrementHandler)
val encodedParams: ArrayList<Pair<String, String>> = arrayListOf()
group?.groupId?.let { encodedParams.add(Constants.QueryConstants.GROUP_ID.urlEncode() to it) }
group?.displayName?.let { encodedParams.add(Constants.QueryConstants.GROUP_DISPLAY_NAME.urlEncode() to it.urlEncode()) }
disposable.add(dataManager.trackAutocompleteSelect(searchTerm,
arrayOf(Constants.QueryConstants.SESSION to sessionId.toString(),
Constants.QueryConstants.AUTOCOMPLETE_SECTION to sectionName,
Constants.QueryConstants.ORIGINAL_QUERY to originalQuery,
Constants.QueryConstants.EVENT to Constants.QueryValues.EVENT_CLICK),
encodedParams.toTypedArray())
.subscribe({
context.broadcastIntent(Constants.EVENT_QUERY_SENT, Constants.EXTRA_TERM to searchTerm)
}, { t ->
t.printStackTrace()
errorCallback?.invoke(t)
e("Autocomplete Select event error: ${t.message}")
}))
val completable = dataManager.trackAutocompleteSelect(searchTerm, arrayOf(
Constants.QueryConstants.AUTOCOMPLETE_SECTION to sectionName,
Constants.QueryConstants.ORIGINAL_QUERY to originalQuery,
Constants.QueryConstants.EVENT to Constants.QueryValues.EVENT_CLICK
), encodedParams.toTypedArray()).subscribeOn(Schedulers.io())

if (this.broadcast) {
completable.subscribeOn(Schedulers.io()).subscribe {
context.broadcastIntent(Constants.EVENT_QUERY_SENT, Constants.EXTRA_TERM to searchTerm)
}
}

return completable
}

fun trackSearchSubmit(searchTerm: String, originalQuery: String, group: Group?, errorCallback: ConstructorError = null) {
val sessionId = preferenceHelper.getSessionId(sessionIncrementEventHandler)
/**
* Tracks search submit events
*/
fun trackSearchSubmit(searchTerm: String, originalQuery: String, group: Group?): Completable {
preferenceHelper.getSessionId(sessionIncrementHandler)
val encodedParams: ArrayList<Pair<String, String>> = arrayListOf()
group?.groupId?.let { encodedParams.add(Constants.QueryConstants.GROUP_ID.urlEncode() to it) }
group?.displayName?.let { encodedParams.add(Constants.QueryConstants.GROUP_DISPLAY_NAME.urlEncode() to it.urlEncode()) }
disposable.add(dataManager.trackSearchSubmit(searchTerm,
arrayOf(Constants.QueryConstants.SESSION to sessionId.toString(),
Constants.QueryConstants.ORIGINAL_QUERY to originalQuery,
Constants.QueryConstants.EVENT to Constants.QueryValues.EVENT_SEARCH), encodedParams.toTypedArray())
.subscribe({
context.broadcastIntent(Constants.EVENT_QUERY_SENT, Constants.EXTRA_TERM to searchTerm)
}, {
it.printStackTrace()
errorCallback?.invoke(it)
e("Search Submit event error: ${it.message}")
}))
val completable = dataManager.trackSearchSubmit(searchTerm, arrayOf(
Constants.QueryConstants.ORIGINAL_QUERY to originalQuery,
Constants.QueryConstants.EVENT to Constants.QueryValues.EVENT_SEARCH
), encodedParams.toTypedArray())

if (this.broadcast) {
completable.subscribeOn(Schedulers.io()).subscribe {
context.broadcastIntent(Constants.EVENT_QUERY_SENT, Constants.EXTRA_TERM to searchTerm)
}
}

return completable
}

fun trackConversion(itemName: String, customerId: String, revenue: Double?, searchTerm: String = Constants.QueryConstants.TERM_UNKNOWN, sectionName: String? = null, errorCallback: ConstructorError = null) {
val sessionId = preferenceHelper.getSessionId(sessionIncrementEventHandler)
val revenueString = revenue?.let { "%.2f".format(revenue) }
disposable.add(dataManager.trackConversion(searchTerm, itemName, customerId, revenueString,
arrayOf(Constants.QueryConstants.SESSION to sessionId.toString(),
Constants.QueryConstants.AUTOCOMPLETE_SECTION to (sectionName ?: preferenceHelper.defaultItemSection))).subscribeOn(Schedulers.io())
.subscribe({}, { t ->
t.printStackTrace()
errorCallback?.invoke(t)
e("Conversion event error: ${t.message}")
}))
/**
* Tracks search results loaded (a.k.a. search results viewed) events
*/
fun trackSearchResultsLoaded(term: String, resultCount: Int): Completable {
preferenceHelper.getSessionId(sessionIncrementHandler)
return dataManager.trackSearchResultsLoaded(term, resultCount, arrayOf(
Constants.QueryConstants.ACTION to Constants.QueryValues.EVENT_SEARCH_RESULTS
))
}

fun trackSearchResultClick(itemName: String, customerId: String, searchTerm: String = Constants.QueryConstants.TERM_UNKNOWN, sectionName: String? = null, errorCallback: ConstructorError = null) {
val sessionId = preferenceHelper.getSessionId(sessionIncrementEventHandler)
/**
* Tracks search result click events
*/
fun trackSearchResultClick(itemName: String, customerId: String, searchTerm: String = Constants.QueryConstants.TERM_UNKNOWN, sectionName: String? = null): Completable {
preferenceHelper.getSessionId(sessionIncrementHandler)
val sName = sectionName ?: preferenceHelper.defaultItemSection
disposable.add(dataManager.trackSearchResultClick(itemName, customerId, searchTerm,
arrayOf(Constants.QueryConstants.SESSION to sessionId.toString(),
Constants.QueryConstants.AUTOCOMPLETE_SECTION to sName)).subscribeOn(Schedulers.io())
.subscribe({}, { t ->
t.printStackTrace()
errorCallback?.invoke(t)
e("Search Result Click event error: ${t.message}")
}))
}
return dataManager.trackSearchResultClick(itemName, customerId, searchTerm, arrayOf(
Constants.QueryConstants.AUTOCOMPLETE_SECTION to sName
))

fun trackSearchResultsLoaded(term: String, resultCount: Int, errorCallback: ConstructorError = null) {
val sessionId = preferenceHelper.getSessionId(sessionIncrementEventHandler)
disposable.add(dataManager.trackSearchResultsLoaded(term, resultCount,
arrayOf(Constants.QueryConstants.SESSION to sessionId.toString(),
Constants.QueryConstants.ACTION to Constants.QueryValues.EVENT_SEARCH_RESULTS)).subscribeOn(Schedulers.io())
.subscribe({}, { t ->
t.printStackTrace()
errorCallback?.invoke(t)
e("Search Results Loaded event error: ${t.message}")
}))
}

fun trackInputFocus(term: String?, errorCallback: ConstructorError = null) {
val sessionId = preferenceHelper.getSessionId(sessionIncrementEventHandler)
disposable.add(dataManager.trackInputFocus(term,
arrayOf(Constants.QueryConstants.SESSION to sessionId.toString(),
Constants.QueryConstants.ACTION to Constants.QueryValues.EVENT_INPUT_FOCUS)).subscribeOn(Schedulers.io())
.subscribe({}, { t ->
t.printStackTrace()
errorCallback?.invoke(t)
e("Input Focus event error: ${t.message}")
}))
/**
* Tracks conversion (a.k.a add to cart) events
*/
fun trackConversion(itemName: String, customerId: String, revenue: Double?, searchTerm: String = Constants.QueryConstants.TERM_UNKNOWN, sectionName: String? = null): Completable {
preferenceHelper.getSessionId(sessionIncrementHandler)
val revenueString = revenue?.let { "%.2f".format(revenue) }
return dataManager.trackConversion(searchTerm, itemName, customerId, revenueString, arrayOf(
Constants.QueryConstants.AUTOCOMPLETE_SECTION to (sectionName ?: preferenceHelper.defaultItemSection)
))
}

fun trackPurchase(clientIds: Array<String>, revenue: Double?, sectionName: String? = null, errorCallback: ConstructorError = null) {
val sessionId = preferenceHelper.getSessionId(sessionIncrementEventHandler)
/**
* Tracks purchase events
*/
fun trackPurchase(clientIds: Array<String>, revenue: Double?, sectionName: String? = null): Completable {
preferenceHelper.getSessionId(sessionIncrementHandler)
val sectionNameParam = sectionName ?: preferenceHelper.defaultItemSection
val revenueString = revenue?.let { "%.2f".format(revenue) }
val params = mutableListOf(Constants.QueryConstants.SESSION to sessionId.toString(),
Constants.QueryConstants.AUTOCOMPLETE_SECTION to sectionNameParam)
disposable.add(dataManager.trackPurchase(clientIds.toList(), revenueString, params.toTypedArray()).subscribeOn(Schedulers.io())
.subscribe({}, { t ->
t.printStackTrace()
errorCallback?.invoke(t)
e("Purchase event error: ${t.message}")
}))
val params = mutableListOf(Constants.QueryConstants.AUTOCOMPLETE_SECTION to sectionNameParam)
return dataManager.trackPurchase(clientIds.toList(), revenueString, params.toTypedArray())
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,39 @@ import io.constructor.BuildConfig
import io.constructor.core.Constants
import io.constructor.data.local.PreferencesHelper
import io.constructor.data.memory.ConfigMemoryHolder
import io.constructor.util.urlEncode
import okhttp3.Interceptor
import okhttp3.Response


/**
* Adds common request query parameters to all API requests
*/
class TokenInterceptor(val context: Context, private val preferencesHelper: PreferencesHelper, private val configMemoryHolder: ConfigMemoryHolder) : Interceptor {

override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
val request = chain.request()
val builder = request.url().newBuilder()
.addQueryParameter(Constants.QueryConstants.API_KEY, preferencesHelper.token)
.addQueryParameter(Constants.QueryConstants.IDENTITY, preferencesHelper.id)
.addQueryParameter(Constants.QueryConstants.TIMESTAMP, System.currentTimeMillis().toString())
.addQueryParameter(Constants.QueryConstants.CLIENT, BuildConfig.CLIENT_VERSION)
.addQueryParameter(Constants.QueryConstants.API_KEY, preferencesHelper.token)
.addQueryParameter(Constants.QueryConstants.IDENTITY, preferencesHelper.id)

// TODO : Urlencode
configMemoryHolder.userId?.let {
builder.addQueryParameter(Constants.QueryConstants.USER_ID, it)
}

builder.addQueryParameter(Constants.QueryConstants.SESSION, preferencesHelper.getSessionId().toString())

// TODO : Urlencode
configMemoryHolder.testCellParams.forEach {
it?.let {
builder.addQueryParameter(it.first, it.second)
}
}
configMemoryHolder.userId?.let {
builder.addQueryParameter(Constants.QueryConstants.USER_ID, it)
}

builder.addQueryParameter(Constants.QueryConstants.CLIENT, BuildConfig.CLIENT_VERSION)
builder.addQueryParameter(Constants.QueryConstants.TIMESTAMP, System.currentTimeMillis().toString())

val url = builder.build()
request = request.newBuilder().url(url).build()
return chain.proceed(request)
val newRequest = request.newBuilder().url(url).build()
return chain.proceed(newRequest)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class NetworkModule(private val context: Context) {
val httpClientBuilder = OkHttpClient.Builder()
httpClientBuilder.addInterceptor(tokenInterceptor)
if (BuildConfig.DEBUG) {
httpClientBuilder.addInterceptor(httpLoggingInterceptor)
// httpClientBuilder.addInterceptor(httpLoggingInterceptor)
}
return httpClientBuilder.build()

Expand Down
2 changes: 0 additions & 2 deletions library/src/main/java/io/constructor/util/Extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ fun String.urlEncode() = URLEncoder.encode(this, "UTF-8").replace("+", "%20")

fun Any.d(msg: String) = Log.d(this::class.qualifiedName, msg)

fun Any.e(msg: String) = Log.e(this::class.qualifiedName, msg)

fun String.base64Encode(): String? {
return String(Base64.encode(toByteArray(), Base64.NO_WRAP or Base64.NO_PADDING))
}
Expand Down
Loading