diff --git a/library/src/main/java/io/constructor/core/ConstructorIo.kt b/library/src/main/java/io/constructor/core/ConstructorIo.kt index 79a8a1e2..5e14cf25 100755 --- a/library/src/main/java/io/constructor/core/ConstructorIo.kt +++ b/library/src/main/java/io/constructor/core/ConstructorIo.kt @@ -21,7 +21,7 @@ import io.constructor.data.model.quiz.* import io.constructor.data.model.recommendations.RecommendationResultClickRequestBody import io.constructor.data.model.recommendations.RecommendationResultViewRequestBody import io.constructor.data.model.recommendations.RecommendationsResponse -import io.constructor.data.model.search.SearchResponse +import io.constructor.data.model.search.* import io.constructor.data.model.tracking.GenericResultClickRequestBody import io.constructor.data.model.tracking.ItemDetailLoadRequestBody import io.constructor.injection.component.AppComponent @@ -1451,18 +1451,38 @@ object ConstructorIo { * @param term the term that results are displayed for, i.e. "Pumpkin" * @param resultCount the number of results for that term * @param customerIds the customerIds of shown items + * @param analyticsTags Additional analytics tags to pass */ - fun trackSearchResultsLoaded(term: String, resultCount: Int, customerIds: Array? = null) { - var completable = trackSearchResultsLoadedInternal(term, resultCount, customerIds) + fun trackSearchResultsLoaded(term: String, resultCount: Int, customerIds: Array? = null, analyticsTags: Map? = null) { + var completable = trackSearchResultsLoadedInternal(term, resultCount, customerIds, analyticsTags) disposable.add(completable.subscribeOn(Schedulers.io()).subscribe({}, { t -> e("Search Results Loaded error: ${t.message}") })) } - internal fun trackSearchResultsLoadedInternal(term: String, resultCount: Int, customerIds: Array? = null): Completable { + internal fun trackSearchResultsLoadedInternal(term: String, resultCount: Int, customerIds: Array? = null, analyticsTags: Map? = null): Completable { preferenceHelper.getSessionId(sessionIncrementHandler) - return dataManager.trackSearchResultsLoaded(term, resultCount, customerIds, arrayOf( - Constants.QueryConstants.ACTION to Constants.QueryValues.EVENT_SEARCH_RESULTS - )) + val items = customerIds?.map{ item -> TrackingItem(item, null)} + val searchResultLoadRequestBody = SearchResultLoadRequestBody( + term, + items, + resultCount, + "Not Available", + BuildConfig.CLIENT_VERSION, + preferenceHelper.id, + preferenceHelper.getSessionId(), + preferenceHelper.apiKey, + configMemoryHolder.userId, + configMemoryHolder.segments, + mergeAnalyticsTags(configMemoryHolder.defaultAnalyticsTags, analyticsTags), + true, + preferenceHelper.defaultItemSection, + System.currentTimeMillis() + ) + + return dataManager.trackSearchResultsLoaded( + searchResultLoadRequestBody, + arrayOf() + ) } /** diff --git a/library/src/main/java/io/constructor/data/DataManager.kt b/library/src/main/java/io/constructor/data/DataManager.kt index c2109d40..d6ec16d0 100755 --- a/library/src/main/java/io/constructor/data/DataManager.kt +++ b/library/src/main/java/io/constructor/data/DataManager.kt @@ -11,7 +11,7 @@ import io.constructor.data.model.quiz.* import io.constructor.data.model.recommendations.RecommendationResultClickRequestBody import io.constructor.data.model.recommendations.RecommendationResultViewRequestBody import io.constructor.data.model.recommendations.RecommendationsResponse -import io.constructor.data.model.search.SearchResponse +import io.constructor.data.model.search.* import io.constructor.data.model.tracking.GenericResultClickRequestBody import io.constructor.data.remote.ApiPaths import io.constructor.data.remote.ConstructorApi @@ -108,8 +108,8 @@ constructor(private val constructorApi: ConstructorApi, @ConstructorSdk private return constructorApi.trackSearchResultClick(term, itemName, customerId, variationId, params.toMap(), encodedParams.toMap()) } - fun trackSearchResultsLoaded(term: String, resultCount: Int, customerIds: Array? = null, params: Array>): Completable { - return constructorApi.trackSearchResultsLoaded(term, resultCount, customerIds?.take(60)?.joinToString(","), params.toMap()) + fun trackSearchResultsLoaded(searchResultLoadRequestBody: SearchResultLoadRequestBody, params: Array>): Completable { + return constructorApi.trackSearchResultsLoaded(searchResultLoadRequestBody, params.toMap()) } fun trackInputFocus(term: String?, params: Array>): Completable { diff --git a/library/src/main/java/io/constructor/data/model/search/SearchResultLoadRequest.kt b/library/src/main/java/io/constructor/data/model/search/SearchResultLoadRequest.kt new file mode 100644 index 00000000..c9b4494b --- /dev/null +++ b/library/src/main/java/io/constructor/data/model/search/SearchResultLoadRequest.kt @@ -0,0 +1,27 @@ +package io.constructor.data.model.search; + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import io.constructor.data.model.common.*; +import java.io.Serializable + +/** + * @suppress + */ +@JsonClass(generateAdapter = true) +data class SearchResultLoadRequestBody( + @Json(name = "search_term") val searchTerm: String, + @Json(name = "items") val items: List?, + @Json(name = "result_count") val resultCount: Int, + @Json(name = "url") val url: String, + @Json(name = "c") val c: String, + @Json(name = "i") val i: String, + @Json(name = "s") val s: Int, + @Json(name = "key") val key: String, + @Json(name = "ui") val ui: String?, + @Json(name = "us") val us: List, + @Json(name = "analytics_tags") val analyticsTags: Map?, + @Json(name= "beacon") val beacon: Boolean?, + @Json(name= "section") val section: String?, + @Json(name= "_dt") val _dt: Long? +) : Serializable diff --git a/library/src/main/java/io/constructor/data/remote/ApiPaths.kt b/library/src/main/java/io/constructor/data/remote/ApiPaths.kt index 913022aa..392b9c54 100755 --- a/library/src/main/java/io/constructor/data/remote/ApiPaths.kt +++ b/library/src/main/java/io/constructor/data/remote/ApiPaths.kt @@ -13,6 +13,7 @@ object ApiPaths { const val URL_SESSION_START_EVENT = "behavior" const val URL_CONVERSION_EVENT = "v2/behavioral_action/conversion" const val URL_SEARCH_RESULT_CLICK_EVENT = "autocomplete/{term}/click_through" + const val URL_SEARCH_RESULT_LOAD_EVENT = "v2/behavioral_action/search_result_load" const val URL_BEHAVIOR = "behavior" const val URL_PURCHASE = "v2/behavioral_action/purchase" const val URL_SEARCH = "search/%s" diff --git a/library/src/main/java/io/constructor/data/remote/ConstructorApi.kt b/library/src/main/java/io/constructor/data/remote/ConstructorApi.kt index 0635c0c3..38789552 100755 --- a/library/src/main/java/io/constructor/data/remote/ConstructorApi.kt +++ b/library/src/main/java/io/constructor/data/remote/ConstructorApi.kt @@ -9,7 +9,7 @@ import io.constructor.data.model.quiz.* import io.constructor.data.model.recommendations.RecommendationResultClickRequestBody import io.constructor.data.model.recommendations.RecommendationResultViewRequestBody import io.constructor.data.model.recommendations.RecommendationsResponse -import io.constructor.data.model.search.SearchResponse +import io.constructor.data.model.search.* import io.constructor.data.model.tracking.GenericResultClickRequestBody import io.reactivex.Completable import io.reactivex.Single @@ -53,10 +53,8 @@ interface ConstructorApi { @QueryMap params: Map, @QueryMap(encoded = true) encodedData: Map): Completable - @GET(ApiPaths.URL_BEHAVIOR) - fun trackSearchResultsLoaded(@Query("term") term: String, - @Query("num_results") resultCount: Int, - @Query("customer_ids") customerIds: String?, + @POST(ApiPaths.URL_SEARCH_RESULT_LOAD_EVENT) + fun trackSearchResultsLoaded(@Body searchRequestBody: SearchResultLoadRequestBody, @QueryMap params: Map): Completable @GET(ApiPaths.URL_BEHAVIOR) diff --git a/library/src/test/java/io/constructor/core/ConstructorIoIntegrationTest.kt b/library/src/test/java/io/constructor/core/ConstructorIoIntegrationTest.kt index a5e6c379..89cf9df1 100644 --- a/library/src/test/java/io/constructor/core/ConstructorIoIntegrationTest.kt +++ b/library/src/test/java/io/constructor/core/ConstructorIoIntegrationTest.kt @@ -973,6 +973,13 @@ class ConstructorIoIntegrationTest { Thread.sleep(timeBetweenTests) } + @Test + fun trackSearchResultsLoadedAgainstRealResponse() { + val observer = constructorIo.trackSearchResultsLoadedInternal("titanic", 10, arrayOf("123", "234")).test() + observer.assertComplete() + Thread.sleep(timeBetweenTests) + } + @Test fun trackConversionAgainstRealResponse() { val observer = constructorIo.trackConversionInternal( diff --git a/library/src/test/java/io/constructor/core/ConstructorIoTrackingTest.kt b/library/src/test/java/io/constructor/core/ConstructorIoTrackingTest.kt index dc7d3f41..d861c892 100755 --- a/library/src/test/java/io/constructor/core/ConstructorIoTrackingTest.kt +++ b/library/src/test/java/io/constructor/core/ConstructorIoTrackingTest.kt @@ -361,7 +361,12 @@ class ConstructorIoTrackingTest { val observer = ConstructorIo.trackSearchResultsLoadedInternal("titanic", 10).test() observer.assertComplete() val request = mockServer.takeRequest() - val path = "/behavior?term=titanic&num_results=10&action=search-results&key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.34.1&_dt=" + val requestBody = getRequestBody(request) + val path = "/v2/behavioral_action/search_result_load?key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.34.1&_dt=" + assertEquals("titanic", requestBody["search_term"]) + assertEquals("10", requestBody["result_count"]) + assertEquals(null, requestBody["items"]) + assertEquals("POST", request.method) assert(request.path!!.startsWith(path)) } @@ -369,10 +374,33 @@ class ConstructorIoTrackingTest { fun trackSearchResultLoadedWithCustomerIDs() { val mockResponse = MockResponse().setResponseCode(204) mockServer.enqueue(mockResponse) - val observer = ConstructorIo.trackSearchResultsLoadedInternal("titanic", 10, arrayOf("TIT-REP-1997", "QE2-REP-1969")).test() + val items = arrayOf("123", "234"); + val observer = ConstructorIo.trackSearchResultsLoadedInternal("titanic", 10, items).test() observer.assertComplete() val request = mockServer.takeRequest() - val path = "/behavior?term=titanic&num_results=10&customer_ids=TIT-REP-1997%2CQE2-REP-1969&action=search-results&key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.34.1&_dt=" + val requestBody = getRequestBody(request) + val path = "/v2/behavioral_action/search_result_load?key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.34.1&_dt=" + assertEquals("titanic", requestBody["search_term"]) + assertEquals("10", requestBody["result_count"]) + assertEquals("[{item_id:123},{item_id:234}]", requestBody["items"]) + assertEquals("POST", request.method) + assert(request.path!!.startsWith(path)) + } + + @Test + fun trackSearchResultLoadedWithAnalyticsTags() { + val mockResponse = MockResponse().setResponseCode(204) + mockServer.enqueue(mockResponse) + val observer = ConstructorIo.trackSearchResultsLoadedInternal("titanic", 10, analyticsTags = mapOf("test" to "test1", "appVersion" to "150")).test() + observer.assertComplete() + val request = mockServer.takeRequest() + val requestBody = getRequestBody(request) + val path = "/v2/behavioral_action/search_result_load?key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.34.1&_dt=" + assertEquals("titanic", requestBody["search_term"]) + assertEquals("10", requestBody["result_count"]) + assertEquals(null, requestBody["items"]) + assertEquals("{appVersion:150,appPlatform:Android,test:test1}", requestBody["analytics_tags"]) + assertEquals("POST", request.method) assert(request.path!!.startsWith(path)) } @@ -383,7 +411,7 @@ class ConstructorIoTrackingTest { val observer = ConstructorIo.trackSearchResultsLoadedInternal("titanic", 10).test() observer.assertError { true } val request = mockServer.takeRequest() - val path = "/behavior?term=titanic&num_results=10&action=search-results&key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.34.1&_dt=" + val path = "/v2/behavioral_action/search_result_load?key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.34.1&_dt=" assert(request.path!!.startsWith(path)) } @@ -394,9 +422,8 @@ class ConstructorIoTrackingTest { mockServer.enqueue(mockResponse) val observer = ConstructorIo.trackSearchResultsLoadedInternal("titanic", 10).test() observer.assertError(SocketTimeoutException::class.java) - val request = mockServer.takeRequest() - val path = "/behavior?term=titanic&num_results=10&action=search-results&key=copper-key&i=wacko-the-guid&ui=player-three&s=67&c=cioand-2.34.1&_dt=" - assert(request.path!!.startsWith(path)) + val request = mockServer.takeRequest(10, TimeUnit.SECONDS) + assertEquals(null, request) } @Test diff --git a/library/src/test/java/io/constructor/core/ConstructorioSegmentsTest.kt b/library/src/test/java/io/constructor/core/ConstructorioSegmentsTest.kt index b94b5c54..ae427263 100644 --- a/library/src/test/java/io/constructor/core/ConstructorioSegmentsTest.kt +++ b/library/src/test/java/io/constructor/core/ConstructorioSegmentsTest.kt @@ -120,7 +120,7 @@ class ConstructorioSegmentsTest { val observer = ConstructorIo.trackSearchResultsLoadedInternal("titanic", 10).test() observer.assertComplete() val request = mockServer.takeRequest() - val path = "/behavior?term=titanic&num_results=10&action=search-results&key=aluminium-key&i=koopa-the-guid&ui=player-two&s=14&us=mobile&us=COUNTRY_US&c=cioand-2.34.1&_dt=" + val path = "/v2/behavioral_action/search_result_load?key=aluminium-key&i=koopa-the-guid&ui=player-two&s=14&us=mobile&us=COUNTRY_US&c=cioand-2.34.1&_dt=" assert(request.path!!.startsWith(path)) } diff --git a/library/src/test/java/io/constructor/core/ConstructorioTestCellTest.kt b/library/src/test/java/io/constructor/core/ConstructorioTestCellTest.kt index 246b6e28..5465a24d 100644 --- a/library/src/test/java/io/constructor/core/ConstructorioTestCellTest.kt +++ b/library/src/test/java/io/constructor/core/ConstructorioTestCellTest.kt @@ -121,7 +121,7 @@ class ConstructorioTestCellTest { val observer = ConstructorIo.trackSearchResultsLoadedInternal("titanic", 10).test() observer.assertComplete() val request = mockServer.takeRequest() - val path = "/behavior?term=titanic&num_results=10&action=search-results&key=aluminium-key&i=koopa-the-guid&ui=player-two&s=14&ef-cellone=vanilla&ef-celltwo=whipped-cream&c=cioand-2.34.1&_dt="; + val path = "/v2/behavioral_action/search_result_load?key=aluminium-key&i=koopa-the-guid&ui=player-two&s=14&ef-cellone=vanilla&ef-celltwo=whipped-cream&c=cioand-2.34.1&_dt="; assert(request.path!!.startsWith(path)) }