From 4bfa7dd98a1e26a9ba560ec40147770fe9859514 Mon Sep 17 00:00:00 2001 From: Tayfun Oztemel Date: Fri, 12 Jul 2024 09:45:33 +0200 Subject: [PATCH 1/6] Update read me. Rearrange api docs --- .gitignore | 1 + README.md | 111 ++++++++++++++++-- .../kotlin/com/crobox/sdk/domain/Campaign.kt | 40 +++++-- .../kotlin/com/crobox/sdk/domain/Promotion.kt | 40 +++++-- .../com/crobox/sdk/domain/PromotionContent.kt | 23 ++-- .../com/crobox/sdk/domain/PromotionContext.kt | 18 ++- .../crobox/sdk/domain/PromotionsResponse.kt | 15 ++- .../kotlin/com/crobox/sdk/PromotionsIT.kt | 59 +++++----- 8 files changed, 229 insertions(+), 78 deletions(-) diff --git a/.gitignore b/.gitignore index 0eae03a..d4cb145 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ build .externalNativeBuild .cxx local.properties +.kotlin/ diff --git a/README.md b/README.md index 3d821dc..1bf9255 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@ # crobox-sdk-android + Crobox SDK for Android [![Build Status](https://github.com/crobox/crobox-sdk-android/actions/workflows/ci.yml/badge.svg)](https://github.com/crobox/crobox-sdk-android/actions?query=main) [![Official Crobox project](https://img.shields.io/badge/project-official-green.svg?colorA=303033&colorB=ff8a2c&label=Crobox)](https://crobox.com/) ![GitHub Release](https://img.shields.io/github/v/release/crobox/crobox-sdk-android?include_prereleases) -This is an asynchronous SDK kit for consuming Crobox API for android applications. Written in Kotlin from the ground up. +This is an asynchronous SDK kit for consuming Crobox API for android applications. Written in Kotlin +from the ground up. First add the dependency to your project: @@ -22,9 +24,12 @@ dependencies { ## Start using Crobox SDK First configure and create a `Crobox` singleton as below, where + - `containerId` should be assigned by Crobox -- `visitorId` should be unique for visitors. It must stay the same across the user's session (or longer if preferred) -- `userId` should optionally be used to link the current visitor with client's user management system, if exists +- `visitorId` should be unique for visitors. It must stay the same across the user's session (or + longer if preferred) +- `userId` should optionally be used to link the current visitor with client's user management + system, if exists ```kotlin import com.crobox.sdk.common.LocaleCode @@ -45,8 +50,10 @@ val croboxInstance = Crobox.getInstance( ``` -RequestQueryParams contains page specific parameters, shared by all requests fired from the same page/view. +RequestQueryParams contains page specific parameters, shared by all requests fired from the same +page/view. It must be recreated when the page/view is displayed. + ```kotlin val overviewPageParams = RequestQueryParams( viewId = UUID.randomUUID(), @@ -54,7 +61,7 @@ val overviewPageParams = RequestQueryParams( ) ``` -For sending events, use the `xyzEvent` APIs exposed by the Crobox instance. +For sending events, use the `xyzEvent` APIs exposed by the Crobox instance. Events might also take event specific parameters: ```kotlin @@ -71,20 +78,21 @@ croboxInstance.clickEvent( } override fun onError(msg: String?) { - Log.d("EventView onError", "" + msg); + Log.d("EventView onError", "" + msg) } } ) ``` -For retrieving promotions for zero, one or more products, use the specific PlaceholderId that is configured with specific page types and linked to campaigns via Crobox Admin App. +For retrieving promotions for zero, one or more products, use the specific PlaceholderId that is +configured with specific page types and linked to campaigns via Crobox Admin App. ```kotlin val promotionsCallback = object : PromotionCallback { - override fun onPromotions(response: PromotionsResponse?) { } + override fun onPromotions(response: PromotionsResponse?) {} - override fun onError(msg: String?) { } + override fun onError(msg: String?) {} } val impressions: List = listOf("001ABC", "002DEF") @@ -112,6 +120,91 @@ croboxInstance.promotions( ) ``` +## Promotion Response Schema + +```kotlin +object : PromotionCallback { + override fun onPromotions(response: PromotionsResponse?) { + // Promotion Context + val context = response?.context + val visitorId = response?.context?.visitorId + val sessionId = response?.context?.sessionId + val groupName = response?.context?.groupName + response?.context?.campaigns?.let { campaigns: List -> + for (campaign in campaigns) { + val campaignId = campaign.id + val campaignName = campaign.name + val variantId = campaign.variantId + val variantName = campaign.variantName + val control = campaign.control + } + } + // Promotions Calculated + response?.promotions?.let { promotions: List -> + for (promotion in promotions) { + val promotionId = promotion.id + val campaignId = promotion.campaignId + val variantId = promotion.variantId + promotion.content?.let { content: PromotionContent -> + val messageId = content.id + val componentName = content.component + val config = content.config + } + } + } + + } + + override fun onError(msg: String?) { + + } +} +``` + +### PromotionsResponse + +| Name | Type | Description | +|------------|------------------|-----------------------------------| +| context | PromotionContext | The context about campaigns | +| promotions | List | The list of promotions calculated | + +### PromotionContext + +| Name | Type | Description | +|-----------|----------------|--------------------------------------------------| +| sessionId | UUID | Session ID | +| visitorId | UUID | Visitor ID | +| groupName | String | The list of campaign and variant names, combined | +| campaigns | List | The list of ongoing campaigns | + +### Campaign + +| Name | Type | Description | +|-------------|---------|------------------------------------------------------------| +| id | String | Campaign ID | +| name | String | Campaign Name | +| variantId | String | Id of the Campaign Variant | +| variantName | String | Name of the Campaign Variant | +| control | Boolean | Indicates if the variant is allocated to the control group | + +### Promotion + +| Name | Type | Description | +|------------|------------------|--------------------------------------------------| +| id | String | Unique id for this promotion | +| productId | String | Product ID that this promotion was requested for | +| campaignId | Int | The campaign which this promotion belongs to | +| variantId | Int | The variant which this promotion belongs to | +| content | PromotionContent | Promotion Content | + +### PromotionContent + +| Name | Type | Description | +|-----------|---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------| +| id | String | Message Id of this promotion | +| component | String | Component Name | +| config | Map | Map of all visual configuration items, managed via Crobox Admin app. Example:
```kotlin Map("Text1_text" : "Best Seller","Text1_color" : "#0e1111")``` | + ## Samples See [test app](app/src/main/kotlin/com/crobox/sdk/testapp/MainActivity.kt) for various samples diff --git a/sdk/src/main/kotlin/com/crobox/sdk/domain/Campaign.kt b/sdk/src/main/kotlin/com/crobox/sdk/domain/Campaign.kt index 1fdeff2..20f1b39 100644 --- a/sdk/src/main/kotlin/com/crobox/sdk/domain/Campaign.kt +++ b/sdk/src/main/kotlin/com/crobox/sdk/domain/Campaign.kt @@ -4,17 +4,35 @@ import com.google.gson.annotations.SerializedName /** * Represents an ongoing Campaign - * - * @property id Campaign ID - * @property name Campaign Name - * @property variantId Id of the Campaign Variant - * @property variantName Name of the Campaign Variant - * @property control Indicates if variant is allocated to the control group */ class Campaign { - @SerializedName("id") val id: String? = null - @SerializedName("name") val name: String? = null - @SerializedName("variantId")val variantId: String? = null - @SerializedName("variantName")val variantName: String? = null - @SerializedName("control")val control: Boolean? = null + /** + * Campaign ID + */ + @SerializedName("id") + val id: String? = null + + /** + * Campaign Name + */ + @SerializedName("name") + val name: String? = null + + /** + * Id of the Campaign Variant + */ + @SerializedName("variantId") + val variantId: String? = null + + /** + * Name of the Campaign Variant + */ + @SerializedName("variantName") + val variantName: String? = null + + /** + * Indicates if variant is allocated to the control group + */ + @SerializedName("control") + val control: Boolean? = null } \ No newline at end of file diff --git a/sdk/src/main/kotlin/com/crobox/sdk/domain/Promotion.kt b/sdk/src/main/kotlin/com/crobox/sdk/domain/Promotion.kt index 8ca852d..2852a5d 100644 --- a/sdk/src/main/kotlin/com/crobox/sdk/domain/Promotion.kt +++ b/sdk/src/main/kotlin/com/crobox/sdk/domain/Promotion.kt @@ -4,18 +4,36 @@ import com.google.gson.annotations.SerializedName /** * Represents a promotion calculated - * - * @property id Unique id for this promotion - * @property productId Product ID that this promotion was requested for - * @property campaignId The campaign which this promotion belongs to - * @property variantId The variant which this promotion belongs to - * @property content Promotion Content */ class Promotion { - @SerializedName("id") val id: String? = null - @SerializedName("productId") val productId: String? = null - @SerializedName("campaignId")val campaignId: Int? = null - @SerializedName("variantId")val variantId: Int? = null - @SerializedName("content") val content: PromotionContent? = null + /** + * Unique id for this promotion + */ + @SerializedName("id") + val id: String? = null + + /** + * Product ID that this promotion was requested for + */ + @SerializedName("productId") + val productId: String? = null + + /** + * The campaign which this promotion belongs to + */ + @SerializedName("campaignId") + val campaignId: Int? = null + + /** + * The variant which this promotion belongs to + */ + @SerializedName("variantId") + val variantId: Int? = null + + /** + * Promotion Content + */ + @SerializedName("content") + val content: PromotionContent? = null } diff --git a/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContent.kt b/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContent.kt index 50e9fbb..ca2c6fc 100644 --- a/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContent.kt +++ b/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContent.kt @@ -2,13 +2,9 @@ package com.crobox.sdk.domain import com.google.gson.annotations.SerializedName -/** - * @property config Map of all visual configuration items, managed via Crobox Admin app - * @property id Message Id of this promotion - */ class PromotionContent { /** - * Visual configuration elements. + * Map of all visual configuration items, managed via Crobox Admin app * * Example: * Map( @@ -16,7 +12,18 @@ class PromotionContent { * "Text1_color" : "#0e1111" * ) */ - @SerializedName("config") val config: Map? = null - @SerializedName("id") val id: String? = null - @SerializedName("component") val component: String? = null + @SerializedName("config") + val config: Map? = null + + /** + * Message Id of this promotion + */ + @SerializedName("id") + val id: String? = null + + /** + * Component Name + */ + @SerializedName("component") + val component: String? = null } \ No newline at end of file diff --git a/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContext.kt b/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContext.kt index 6a2b15f..044e7f3 100644 --- a/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContext.kt +++ b/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContext.kt @@ -5,23 +5,29 @@ import java.util.UUID /** * The context about campaigns - * - * @property campaigns The list of ongoing campaigns - * @property sessionId Session ID - * @property visitorId Visitor ID - * @property groupName List of campaign and variant names, combined - * */ class PromotionContext { + /** + * The list of ongoing campaigns + */ @SerializedName("experiments") val campaigns: List? = null + /** + * Session ID + */ @SerializedName("sid") val sessionId: UUID? = null + /** + * Visitor ID + */ @SerializedName("pid") val visitorId: UUID? = null + /** + * The list of campaign and variant names, combined + */ @SerializedName("groupName") val groupName: String? = null } \ No newline at end of file diff --git a/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionsResponse.kt b/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionsResponse.kt index 21b9548..6985604 100644 --- a/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionsResponse.kt +++ b/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionsResponse.kt @@ -4,11 +4,14 @@ import com.google.gson.annotations.SerializedName /** * Promotion Result - * - * @property context The context about campaigns - * @property promotions The promotions calculated */ -class PromotionsResponse( - @SerializedName("context") val context: PromotionContext? = null, - @SerializedName("promotions") val promotions: List? = null +data class PromotionsResponse( + /** + * The context about campaigns + */ + @SerializedName("context") val context: PromotionContext, + /** + * The promotions calculated + */ + @SerializedName("promotions") val promotions: List ) \ No newline at end of file diff --git a/sdk/src/test/kotlin/com/crobox/sdk/PromotionsIT.kt b/sdk/src/test/kotlin/com/crobox/sdk/PromotionsIT.kt index 4215770..24d8d1e 100644 --- a/sdk/src/test/kotlin/com/crobox/sdk/PromotionsIT.kt +++ b/sdk/src/test/kotlin/com/crobox/sdk/PromotionsIT.kt @@ -6,6 +6,9 @@ import com.crobox.sdk.config.CroboxConfig import com.crobox.sdk.core.Crobox import com.crobox.sdk.data.model.PageType import com.crobox.sdk.data.model.RequestQueryParams +import com.crobox.sdk.domain.Campaign +import com.crobox.sdk.domain.Promotion +import com.crobox.sdk.domain.PromotionContent import com.crobox.sdk.domain.PromotionsResponse import com.crobox.sdk.presenter.PromotionCallback import org.junit.After @@ -46,36 +49,38 @@ class PromotionsIT { private val stubPromotionCallback = object : PromotionCallback { override fun onPromotions(response: PromotionsResponse?) { - val contextStr = - """ - Context.VisitorId: ${response?.context?.visitorId} - Context.SessionId: ${response?.context?.sessionId} - Context.Campaigns: ${ - response?.context?.campaigns?.map { campaign -> - "[Id: ${campaign.id}, Name: ${campaign.name}]" - }?.joinToString(",") - } - """.trimIndent() - - val promotionsStr = response?.promotions?.map { promotion -> - """ - Promotion[ - Id:${promotion.id} - Product:${promotion.productId} - Campaign:${promotion.campaignId} - Variant:${promotion.variantId} - Msg Id:${promotion.content?.id} - Msg Component:${promotion.content?.component} - Msg Config:${promotion.content?.config} - ] - """.trimIndent() - }?.joinToString(",") - - println("$contextStr\n$promotionsStr") + val context = response?.context + val visitorId = response?.context?.visitorId + val sessionId = response?.context?.sessionId + val groupName = response?.context?.groupName + response?.context?.campaigns?.let { campaigns: List -> + for (campaign in campaigns) { + val campaignId = campaign.id + val campaignName = campaign.name + val variantId = campaign.variantId + val variantName = campaign.variantName + val control = campaign.control + } + } + + response?.promotions?.let { promotions: List -> + for (promotion in promotions) { + val promotionId = promotion.id + val campaignId = promotion.campaignId + val variantId = promotion.variantId + promotion.content?.let { content: PromotionContent -> + val messageId = content.id + val componentName = content.component + val config = content.config + + } + + } + } } override fun onError(msg: String?) { - println("Promotion failed with $msg") + } } From a302ba6911a12015086041447025c985567cd4f3 Mon Sep 17 00:00:00 2001 From: Tayfun Oztemel Date: Fri, 12 Jul 2024 09:50:02 +0200 Subject: [PATCH 2/6] Update table --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1bf9255..d156bc0 100644 --- a/README.md +++ b/README.md @@ -199,11 +199,11 @@ object : PromotionCallback { ### PromotionContent -| Name | Type | Description | -|-----------|---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------| -| id | String | Message Id of this promotion | -| component | String | Component Name | -| config | Map | Map of all visual configuration items, managed via Crobox Admin app. Example:
```kotlin Map("Text1_text" : "Best Seller","Text1_color" : "#0e1111")``` | +| Name | Type | Description | +|-----------|---------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------| +| id | String | Message Id of this promotion | +| component | String | Component Name | +| config | Map | Map of all visual configuration items, managed via Crobox Admin app.
Example:
```Map("Text1_text" : "Best Seller", "Text1_color" : "#0e1111")``` | ## Samples From 5ff777cd44c8c8d34c03f3e4696958a1d1769495 Mon Sep 17 00:00:00 2001 From: Tayfun Oztemel Date: Fri, 12 Jul 2024 12:00:05 +0200 Subject: [PATCH 3/6] Clean up optionals from the model --- README.md | 79 ++++++------ .../com/crobox/sdk/testapp/MainActivity.kt | 75 ++++++----- .../sdk/{domain => data/api}/Constant.kt | 2 +- .../crobox/sdk/data/api/CroboxAPIClient.kt | 6 +- .../kotlin/com/crobox/sdk/domain/Campaign.kt | 14 +-- .../kotlin/com/crobox/sdk/domain/Promotion.kt | 16 +-- .../com/crobox/sdk/domain/PromotionContent.kt | 10 +- .../com/crobox/sdk/domain/PromotionContext.kt | 12 +- .../sdk/presenter/CroboxAPIPresenter.kt | 33 ++--- .../crobox/sdk/presenter/PromotionCallback.kt | 4 +- .../kotlin/com/crobox/sdk/PromotionsIT.kt | 117 ++++++++++-------- 11 files changed, 200 insertions(+), 168 deletions(-) rename sdk/src/main/kotlin/com/crobox/sdk/{domain => data/api}/Constant.kt (70%) diff --git a/README.md b/README.md index d156bc0..d2ce934 100644 --- a/README.md +++ b/README.md @@ -124,40 +124,37 @@ croboxInstance.promotions( ```kotlin object : PromotionCallback { - override fun onPromotions(response: PromotionsResponse?) { - // Promotion Context - val context = response?.context - val visitorId = response?.context?.visitorId - val sessionId = response?.context?.sessionId - val groupName = response?.context?.groupName - response?.context?.campaigns?.let { campaigns: List -> - for (campaign in campaigns) { - val campaignId = campaign.id - val campaignName = campaign.name - val variantId = campaign.variantId - val variantName = campaign.variantName - val control = campaign.control - } - } - // Promotions Calculated - response?.promotions?.let { promotions: List -> - for (promotion in promotions) { - val promotionId = promotion.id - val campaignId = promotion.campaignId - val variantId = promotion.variantId - promotion.content?.let { content: PromotionContent -> - val messageId = content.id - val componentName = content.component - val config = content.config + override fun onPromotions(response: PromotionsResponse) { + val context = response.context + val promotions = response.promotions + + val visitorId = context.visitorId + val sessionId = context.sessionId + val groupName = context.groupName ?: "" + for (campaign in context.campaigns) { + val campaignId = campaign.id + val campaignName = campaign.name + val variantId = campaign.variantId + val variantName = campaign.variantName + val control = campaign.control } - } - } - - } - override fun onError(msg: String?) { - - } + for (promotion in promotions) { + val promotionId = promotion.id + val campaignId = promotion.campaignId + val variantId = promotion.variantId + val productId = promotion.productId ?: "" + promotion.content?.let { content: PromotionContent -> + val messageId = content.messageId + val componentName = content.component + val configMap = content.config + for (c in configMap) { + val configKey = c.key + val configValue = c.value + } + } + } + } } ``` @@ -174,7 +171,7 @@ object : PromotionCallback { |-----------|----------------|--------------------------------------------------| | sessionId | UUID | Session ID | | visitorId | UUID | Visitor ID | -| groupName | String | The list of campaign and variant names, combined | +| groupName | String? | The list of campaign and variant names, combined | | campaigns | List | The list of ongoing campaigns | ### Campaign @@ -189,19 +186,19 @@ object : PromotionCallback { ### Promotion -| Name | Type | Description | -|------------|------------------|--------------------------------------------------| -| id | String | Unique id for this promotion | -| productId | String | Product ID that this promotion was requested for | -| campaignId | Int | The campaign which this promotion belongs to | -| variantId | Int | The variant which this promotion belongs to | -| content | PromotionContent | Promotion Content | +| Name | Type | Description | +|------------|-------------------|----------------------------------------------| +| id | String | Unique id for this promotion | +| productId | String? | Product ID if requested | +| campaignId | Int | The campaign which this promotion belongs to | +| variantId | Int | The variant which this promotion belongs to | +| content | PromotionContent? | Promotion Content | ### PromotionContent | Name | Type | Description | |-----------|---------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------| -| id | String | Message Id of this promotion | +| messageId | String | Message Id of this promotion | | component | String | Component Name | | config | Map | Map of all visual configuration items, managed via Crobox Admin app.
Example:
```Map("Text1_text" : "Best Seller", "Text1_color" : "#0e1111")``` | diff --git a/app/src/main/kotlin/com/crobox/sdk/testapp/MainActivity.kt b/app/src/main/kotlin/com/crobox/sdk/testapp/MainActivity.kt index 9912bc2..f1d9f11 100644 --- a/app/src/main/kotlin/com/crobox/sdk/testapp/MainActivity.kt +++ b/app/src/main/kotlin/com/crobox/sdk/testapp/MainActivity.kt @@ -3,11 +3,10 @@ package com.crobox.sdk.testapp import android.os.Bundle import android.util.Log import androidx.appcompat.app.AppCompatActivity - import com.crobox.sdk.common.CurrencyCode import com.crobox.sdk.common.LocaleCode -import com.crobox.sdk.core.Crobox import com.crobox.sdk.config.CroboxConfig +import com.crobox.sdk.core.Crobox import com.crobox.sdk.data.model.CartQueryParams import com.crobox.sdk.data.model.ClickQueryParams import com.crobox.sdk.data.model.ErrorQueryParams @@ -23,7 +22,8 @@ class MainActivity : AppCompatActivity() { private val containerId = "xlrc9t" // Collection of products/impressions - private val impressions: List = listOf("product1", "product2", "product3", "product4", "product5") + private val impressions: List = + listOf("product1", "product2", "product3", "product4", "product5") private val productId = impressions.get(0) // CroboxInstance is the single point of all interactions, keeping the configuration and providing all functionality @@ -114,41 +114,52 @@ class MainActivity : AppCompatActivity() { val stubPromotionCallback = object : PromotionCallback { val TAG = "PromotionCallback" - override fun onPromotions(response: PromotionsResponse?) { - val campaigns: List? = - response?.context?.campaigns?.map { campaign -> - "Campaign[Id: ${campaign.id}, Name: ${campaign.name}]" - } + override fun onPromotions(response: PromotionsResponse) { + val context = response.context + val promotions = response.promotions - Log.d( - TAG, + val visitorId = context.visitorId + val sessionId = context.sessionId + val campaigns = context.campaigns.map { campaign -> + "Campaign[Id: ${campaign.id}, Name: ${campaign.name}]" + } + val contextStr = """ - Context [ - VisitorId: ${response?.context?.visitorId} - SessionId: ${response?.context?.sessionId} - Campaigns: ${campaigns?.joinToString()} - ] - """.trimIndent() - ) + Context [ + VisitorId: $visitorId, + SessionId: $sessionId + Campaigns: ${campaigns.joinToString()} + ] + """.trimIndent() + + val promotionsStr = promotions.map { promotion -> + val promotionId = promotion.id + val campaignId = promotion.campaignId + val variantId = promotion.variantId + val productId = promotion.productId ?: "" - response?.promotions?.forEach { promotion -> - Log.d( - TAG, - """ - Promotion[ - Id:${promotion.id} - Product:${promotion.productId} - Campaign:${promotion.campaignId} - Variant:${promotion.variantId} - Msg Id:${promotion.content?.id} - Msg Config:${promotion.content?.config} - ] - """.trimIndent() - ) + """ + Promotion[ + Id:$promotionId + Product:$productId + Campaign:$campaignId + Variant:$variantId + Msg Id:${promotion.content?.messageId} + Component:${promotion.content?.component} + Msg Config:${promotion.content?.config} + ] + """.trimIndent() } + + Log.d( + TAG, """ + context: $contextStr, + promotions: ${promotionsStr.joinToString()} + """.trimIndent() + ) } - override fun onError(msg: String?) { + override fun onError(msg: String) { Log.d(TAG, "Promotion failed with $msg") } } diff --git a/sdk/src/main/kotlin/com/crobox/sdk/domain/Constant.kt b/sdk/src/main/kotlin/com/crobox/sdk/data/api/Constant.kt similarity index 70% rename from sdk/src/main/kotlin/com/crobox/sdk/domain/Constant.kt rename to sdk/src/main/kotlin/com/crobox/sdk/data/api/Constant.kt index 7ae5d5c..c1f5953 100644 --- a/sdk/src/main/kotlin/com/crobox/sdk/domain/Constant.kt +++ b/sdk/src/main/kotlin/com/crobox/sdk/data/api/Constant.kt @@ -1,4 +1,4 @@ -package com.crobox.sdk.domain +package com.crobox.sdk.data.api internal object Constant { const val API_URL = "https://api.crobox.com" diff --git a/sdk/src/main/kotlin/com/crobox/sdk/data/api/CroboxAPIClient.kt b/sdk/src/main/kotlin/com/crobox/sdk/data/api/CroboxAPIClient.kt index fb9145b..fe5bca6 100644 --- a/sdk/src/main/kotlin/com/crobox/sdk/data/api/CroboxAPIClient.kt +++ b/sdk/src/main/kotlin/com/crobox/sdk/data/api/CroboxAPIClient.kt @@ -1,9 +1,9 @@ package com.crobox.sdk.data.api -import com.crobox.sdk.domain.Constant import com.google.gson.GsonBuilder import com.google.gson.Strictness import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import java.util.UUID @@ -27,7 +27,9 @@ internal object CroboxAPIClient { } private fun client(): OkHttpClient { - val client: OkHttpClient = OkHttpClient.Builder().readTimeout(60 * 5, TimeUnit.SECONDS) + val client: OkHttpClient = OkHttpClient.Builder() + .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) + .readTimeout(60 * 5, TimeUnit.SECONDS) .connectTimeout(60 * 5, TimeUnit.SECONDS).build() return client diff --git a/sdk/src/main/kotlin/com/crobox/sdk/domain/Campaign.kt b/sdk/src/main/kotlin/com/crobox/sdk/domain/Campaign.kt index 20f1b39..7f2ba88 100644 --- a/sdk/src/main/kotlin/com/crobox/sdk/domain/Campaign.kt +++ b/sdk/src/main/kotlin/com/crobox/sdk/domain/Campaign.kt @@ -5,34 +5,34 @@ import com.google.gson.annotations.SerializedName /** * Represents an ongoing Campaign */ -class Campaign { +data class Campaign ( /** * Campaign ID */ @SerializedName("id") - val id: String? = null + val id: String, /** * Campaign Name */ @SerializedName("name") - val name: String? = null + val name: String, /** * Id of the Campaign Variant */ @SerializedName("variantId") - val variantId: String? = null + val variantId: String, /** * Name of the Campaign Variant */ @SerializedName("variantName") - val variantName: String? = null + val variantName: String, /** * Indicates if variant is allocated to the control group */ @SerializedName("control") - val control: Boolean? = null -} \ No newline at end of file + val control: Boolean +) \ No newline at end of file diff --git a/sdk/src/main/kotlin/com/crobox/sdk/domain/Promotion.kt b/sdk/src/main/kotlin/com/crobox/sdk/domain/Promotion.kt index 2852a5d..baf3ce0 100644 --- a/sdk/src/main/kotlin/com/crobox/sdk/domain/Promotion.kt +++ b/sdk/src/main/kotlin/com/crobox/sdk/domain/Promotion.kt @@ -1,39 +1,39 @@ package com.crobox.sdk.domain import com.google.gson.annotations.SerializedName +import java.util.UUID /** * Represents a promotion calculated */ -class Promotion { +data class Promotion ( /** * Unique id for this promotion */ @SerializedName("id") - val id: String? = null + val id: UUID, /** * Product ID that this promotion was requested for */ @SerializedName("productId") - val productId: String? = null + val productId: String?, /** * The campaign which this promotion belongs to */ @SerializedName("campaignId") - val campaignId: Int? = null + val campaignId: Int, /** * The variant which this promotion belongs to */ @SerializedName("variantId") - val variantId: Int? = null + val variantId: Int, /** * Promotion Content */ @SerializedName("content") - val content: PromotionContent? = null -} - + val content: PromotionContent? +) \ No newline at end of file diff --git a/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContent.kt b/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContent.kt index ca2c6fc..5c643ae 100644 --- a/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContent.kt +++ b/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContent.kt @@ -2,7 +2,7 @@ package com.crobox.sdk.domain import com.google.gson.annotations.SerializedName -class PromotionContent { +data class PromotionContent ( /** * Map of all visual configuration items, managed via Crobox Admin app * @@ -13,17 +13,17 @@ class PromotionContent { * ) */ @SerializedName("config") - val config: Map? = null + val config: Map, /** * Message Id of this promotion */ @SerializedName("id") - val id: String? = null + val messageId: String, /** * Component Name */ @SerializedName("component") - val component: String? = null -} \ No newline at end of file + val component: String +) \ No newline at end of file diff --git a/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContext.kt b/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContext.kt index 044e7f3..5a993f7 100644 --- a/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContext.kt +++ b/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContext.kt @@ -6,28 +6,28 @@ import java.util.UUID /** * The context about campaigns */ -class PromotionContext { +data class PromotionContext( /** * The list of ongoing campaigns */ @SerializedName("experiments") - val campaigns: List? = null + val campaigns: List, /** * Session ID */ @SerializedName("sid") - val sessionId: UUID? = null + val sessionId: UUID, /** * Visitor ID */ @SerializedName("pid") - val visitorId: UUID? = null + val visitorId: UUID, /** * The list of campaign and variant names, combined */ @SerializedName("groupName") - val groupName: String? = null -} \ No newline at end of file + val groupName: String? +) \ No newline at end of file diff --git a/sdk/src/main/kotlin/com/crobox/sdk/presenter/CroboxAPIPresenter.kt b/sdk/src/main/kotlin/com/crobox/sdk/presenter/CroboxAPIPresenter.kt index 69d0ffd..0bbee01 100644 --- a/sdk/src/main/kotlin/com/crobox/sdk/presenter/CroboxAPIPresenter.kt +++ b/sdk/src/main/kotlin/com/crobox/sdk/presenter/CroboxAPIPresenter.kt @@ -43,21 +43,24 @@ internal class CroboxAPIPresenter(private val config: CroboxConfig) { call: Call, response: Response ) { try { + // http [200...300) if (response.isSuccessful) { - promotionCallback.onPromotions(response.body()) + response.body()?.let { promotionCallback.onPromotions(it) } + ?: promotionCallback.onError("Successful http but null body") } else { + // http errors [300...600) promotionCallback.onError(response.body().toString()) CroboxDebug.promotionError(response.body().toString()) } } catch (ex: Exception) { promotionCallback.onError(ex.message ?: "") - CroboxDebug.promotionError(ex.message.toString()) + CroboxDebug.promotionError(ex.message ?: "") } } override fun onFailure(call: Call, t: Throwable) { promotionCallback.onError(t.message ?: "") - CroboxDebug.promotionError(t.message.toString()) + CroboxDebug.promotionError(t.message ?: "") } }) } @@ -70,22 +73,22 @@ internal class CroboxAPIPresenter(private val config: CroboxConfig) { val stringParameters = parameters.mapValues { it.value.toString() } apiInterface.event(stringParameters)?.enqueue(object : Callback { - override fun onResponse( - call: Call, response: Response - ) { - try { - if (!response.isSuccessful) { - CroboxDebug.eventError(response.body().toString()) - } - } catch (ex: Exception) { + override fun onResponse( + call: Call, response: Response + ) { + try { + if (!response.isSuccessful) { CroboxDebug.eventError(response.body().toString()) } + } catch (ex: Exception) { + CroboxDebug.eventError(response.body().toString()) } + } - override fun onFailure(call: Call, t: Throwable) { - CroboxDebug.eventError(t.message.toString()) - } - }) + override fun onFailure(call: Call, t: Throwable) { + CroboxDebug.eventError(t.message.toString()) + } + }) } private fun eventQuery( diff --git a/sdk/src/main/kotlin/com/crobox/sdk/presenter/PromotionCallback.kt b/sdk/src/main/kotlin/com/crobox/sdk/presenter/PromotionCallback.kt index fe205fb..65d27d1 100644 --- a/sdk/src/main/kotlin/com/crobox/sdk/presenter/PromotionCallback.kt +++ b/sdk/src/main/kotlin/com/crobox/sdk/presenter/PromotionCallback.kt @@ -3,6 +3,6 @@ package com.crobox.sdk.presenter import com.crobox.sdk.domain.PromotionsResponse interface PromotionCallback { - fun onPromotions(response: PromotionsResponse?) {} - fun onError(msg: String?) + fun onPromotions(response: PromotionsResponse) {} + fun onError(msg: String) } diff --git a/sdk/src/test/kotlin/com/crobox/sdk/PromotionsIT.kt b/sdk/src/test/kotlin/com/crobox/sdk/PromotionsIT.kt index 24d8d1e..235f5d4 100644 --- a/sdk/src/test/kotlin/com/crobox/sdk/PromotionsIT.kt +++ b/sdk/src/test/kotlin/com/crobox/sdk/PromotionsIT.kt @@ -6,19 +6,21 @@ import com.crobox.sdk.config.CroboxConfig import com.crobox.sdk.core.Crobox import com.crobox.sdk.data.model.PageType import com.crobox.sdk.data.model.RequestQueryParams -import com.crobox.sdk.domain.Campaign -import com.crobox.sdk.domain.Promotion -import com.crobox.sdk.domain.PromotionContent import com.crobox.sdk.domain.PromotionsResponse import com.crobox.sdk.presenter.PromotionCallback import org.junit.After +import org.junit.Assert import org.junit.Test import java.util.UUID +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit class PromotionsIT { - private val vid = UUID.randomUUID() - private val pid = UUID.randomUUID() + object Conf { + val vid: UUID = UUID.randomUUID() + val pid: UUID = UUID.randomUUID() + } // FL TEST private val containerId = "xlrc9t" @@ -28,14 +30,14 @@ class PromotionsIT { private val croboxInstance = Crobox.getInstance( CroboxConfig( containerId = containerId, - visitorId = pid, + visitorId = Conf.pid, currencyCode = CurrencyCode.USD, localeCode = LocaleCode.EN_US ) ) private val overviewPageParams = RequestQueryParams( - viewId = vid, + viewId = Conf.vid, pageType = PageType.PageOverview ) @@ -47,43 +49,6 @@ class PromotionsIT { private val impressions: List = listOf("product1", "product2", "product3", "product4", "product5") - private val stubPromotionCallback = object : PromotionCallback { - override fun onPromotions(response: PromotionsResponse?) { - val context = response?.context - val visitorId = response?.context?.visitorId - val sessionId = response?.context?.sessionId - val groupName = response?.context?.groupName - response?.context?.campaigns?.let { campaigns: List -> - for (campaign in campaigns) { - val campaignId = campaign.id - val campaignName = campaign.name - val variantId = campaign.variantId - val variantName = campaign.variantName - val control = campaign.control - } - } - - response?.promotions?.let { promotions: List -> - for (promotion in promotions) { - val promotionId = promotion.id - val campaignId = promotion.campaignId - val variantId = promotion.variantId - promotion.content?.let { content: PromotionContent -> - val messageId = content.id - val componentName = content.component - val config = content.config - - } - - } - } - } - - override fun onError(msg: String?) { - - } - } - @After fun after() { Thread.sleep(2000) @@ -91,31 +56,85 @@ class PromotionsIT { @Test fun testMultipleProducts() { + val lock = CountDownLatch(1); + var visitorId: UUID? = null + var sessionId: UUID? = null croboxInstance.promotions( placeholderId = placeholderId, queryParams = overviewPageParams, impressions = impressions, - promotionCallback = stubPromotionCallback + promotionCallback = object : PromotionCallback { + override fun onPromotions(response: PromotionsResponse) { + println(response) + visitorId = response.context.visitorId + sessionId = response.context.sessionId + lock.countDown() + } + + override fun onError(msg: String) { + println("Error:$msg") + lock.countDown() + } + } ) + lock.await(2, TimeUnit.SECONDS) + Assert.assertEquals(Conf.pid, visitorId) + Assert.assertNotNull(sessionId) } @Test fun testOneProduct() { + val lock = CountDownLatch(1); + var visitorId: UUID? = null + var sessionId: UUID? = null croboxInstance.promotions( placeholderId = placeholderId2, queryParams = detailPageParams, impressions = impressions.subList(0, 1), - promotionCallback = stubPromotionCallback + promotionCallback = object : PromotionCallback { + override fun onPromotions(response: PromotionsResponse) { + println(response) + visitorId = response.context.visitorId + sessionId = response.context.sessionId + lock.countDown() + } + + override fun onError(msg: String) { + println("Error:$msg") + lock.countDown() + } + } ) + lock.await(2, TimeUnit.SECONDS) + Assert.assertEquals(Conf.pid, visitorId) + Assert.assertNotNull(sessionId) } @Test fun testNoProduct() { + val lock = CountDownLatch(1); + var visitorId: UUID? = null + var sessionId: UUID? = null croboxInstance.promotions( - placeholderId = 1, - queryParams = detailPageParams, - promotionCallback = stubPromotionCallback + placeholderId = placeholderId, + queryParams = overviewPageParams, + promotionCallback = object : PromotionCallback { + override fun onPromotions(response: PromotionsResponse) { + println(response) + visitorId = response.context.visitorId + sessionId = response.context.sessionId + lock.countDown() + } + + override fun onError(msg: String) { + println("Error:$msg") + lock.countDown() + } + } ) + lock.await(2, TimeUnit.SECONDS) + Assert.assertEquals(Conf.pid, visitorId) + Assert.assertNotNull(sessionId) } } \ No newline at end of file From 95473ac832de8e6e069fa82775ab59a60e8f594e Mon Sep 17 00:00:00 2001 From: Tayfun Oztemel Date: Fri, 12 Jul 2024 14:51:46 +0200 Subject: [PATCH 4/6] update README --- README.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index d2ce934..155c127 100644 --- a/README.md +++ b/README.md @@ -176,29 +176,29 @@ object : PromotionCallback { ### Campaign -| Name | Type | Description | -|-------------|---------|------------------------------------------------------------| -| id | String | Campaign ID | -| name | String | Campaign Name | -| variantId | String | Id of the Campaign Variant | -| variantName | String | Name of the Campaign Variant | -| control | Boolean | Indicates if the variant is allocated to the control group | +| Name | Type | Description | +|-------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| id | String | Campaign ID | +| name | String | Campaign Name | +| variantId | String | There is a ratio that determines the amount of traffic exposed to this campaign (or is allocated to the control group) between Crobox and Control group. Variant id refers to the variant which this promotion belongs to and is used for debugging | +| variantName | String | Name of the Campaign Variant | +| control | Boolean | Indicates if the variant is allocated to the control group | ### Promotion -| Name | Type | Description | -|------------|-------------------|----------------------------------------------| -| id | String | Unique id for this promotion | -| productId | String? | Product ID if requested | -| campaignId | Int | The campaign which this promotion belongs to | -| variantId | Int | The variant which this promotion belongs to | -| content | PromotionContent? | Promotion Content | +| Name | Type | Description | +|------------|-------------------|------------------------------------------------------| +| id | String | Unique id for this promotion | +| productId | String? | Product ID if requested | +| campaignId | Int | The campaign which this promotion belongs to | +| variantId | Int | ID of the variant that this promotion is assigned to | +| content | PromotionContent? | Promotion Content | ### PromotionContent | Name | Type | Description | |-----------|---------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------| -| messageId | String | Message Id of this promotion | +| messageId | String | As Campaigns might have alternative messages, Message Id identifies the message assigned to this promotion | | component | String | Component Name | | config | Map | Map of all visual configuration items, managed via Crobox Admin app.
Example:
```Map("Text1_text" : "Best Seller", "Text1_color" : "#0e1111")``` | From 7183147bd4b69dda53174ebf2ca221b7ce8d24dc Mon Sep 17 00:00:00 2001 From: Tayfun Oztemel Date: Fri, 12 Jul 2024 14:54:51 +0200 Subject: [PATCH 5/6] clean up --- sdk/src/main/kotlin/com/crobox/sdk/data/api/CroboxAPIClient.kt | 2 -- sdk/src/main/kotlin/com/crobox/sdk/domain/Campaign.kt | 2 +- sdk/src/main/kotlin/com/crobox/sdk/domain/Promotion.kt | 2 +- sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContent.kt | 2 +- sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContext.kt | 2 +- sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionsResponse.kt | 2 +- 6 files changed, 5 insertions(+), 7 deletions(-) diff --git a/sdk/src/main/kotlin/com/crobox/sdk/data/api/CroboxAPIClient.kt b/sdk/src/main/kotlin/com/crobox/sdk/data/api/CroboxAPIClient.kt index fe5bca6..91950d3 100644 --- a/sdk/src/main/kotlin/com/crobox/sdk/data/api/CroboxAPIClient.kt +++ b/sdk/src/main/kotlin/com/crobox/sdk/data/api/CroboxAPIClient.kt @@ -3,7 +3,6 @@ package com.crobox.sdk.data.api import com.google.gson.GsonBuilder import com.google.gson.Strictness import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import java.util.UUID @@ -28,7 +27,6 @@ internal object CroboxAPIClient { private fun client(): OkHttpClient { val client: OkHttpClient = OkHttpClient.Builder() - .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) .readTimeout(60 * 5, TimeUnit.SECONDS) .connectTimeout(60 * 5, TimeUnit.SECONDS).build() diff --git a/sdk/src/main/kotlin/com/crobox/sdk/domain/Campaign.kt b/sdk/src/main/kotlin/com/crobox/sdk/domain/Campaign.kt index 7f2ba88..65f9cae 100644 --- a/sdk/src/main/kotlin/com/crobox/sdk/domain/Campaign.kt +++ b/sdk/src/main/kotlin/com/crobox/sdk/domain/Campaign.kt @@ -35,4 +35,4 @@ data class Campaign ( */ @SerializedName("control") val control: Boolean -) \ No newline at end of file +) diff --git a/sdk/src/main/kotlin/com/crobox/sdk/domain/Promotion.kt b/sdk/src/main/kotlin/com/crobox/sdk/domain/Promotion.kt index baf3ce0..e40c1ca 100644 --- a/sdk/src/main/kotlin/com/crobox/sdk/domain/Promotion.kt +++ b/sdk/src/main/kotlin/com/crobox/sdk/domain/Promotion.kt @@ -36,4 +36,4 @@ data class Promotion ( */ @SerializedName("content") val content: PromotionContent? -) \ No newline at end of file +) diff --git a/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContent.kt b/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContent.kt index 5c643ae..95cfcd0 100644 --- a/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContent.kt +++ b/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContent.kt @@ -26,4 +26,4 @@ data class PromotionContent ( */ @SerializedName("component") val component: String -) \ No newline at end of file +) diff --git a/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContext.kt b/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContext.kt index 5a993f7..5033ef8 100644 --- a/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContext.kt +++ b/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionContext.kt @@ -30,4 +30,4 @@ data class PromotionContext( */ @SerializedName("groupName") val groupName: String? -) \ No newline at end of file +) diff --git a/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionsResponse.kt b/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionsResponse.kt index 6985604..8f92c0d 100644 --- a/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionsResponse.kt +++ b/sdk/src/main/kotlin/com/crobox/sdk/domain/PromotionsResponse.kt @@ -14,4 +14,4 @@ data class PromotionsResponse( * The promotions calculated */ @SerializedName("promotions") val promotions: List -) \ No newline at end of file +) From f764bd8567aca7dbef9ce0b14547356d0f1e2072 Mon Sep 17 00:00:00 2001 From: Tayfun Oztemel Date: Fri, 12 Jul 2024 15:30:24 +0200 Subject: [PATCH 6/6] ignore integration tests --- sdk/src/test/kotlin/com/crobox/sdk/PromotionsIT.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sdk/src/test/kotlin/com/crobox/sdk/PromotionsIT.kt b/sdk/src/test/kotlin/com/crobox/sdk/PromotionsIT.kt index 235f5d4..7dd0a32 100644 --- a/sdk/src/test/kotlin/com/crobox/sdk/PromotionsIT.kt +++ b/sdk/src/test/kotlin/com/crobox/sdk/PromotionsIT.kt @@ -10,11 +10,13 @@ import com.crobox.sdk.domain.PromotionsResponse import com.crobox.sdk.presenter.PromotionCallback import org.junit.After import org.junit.Assert +import org.junit.Ignore import org.junit.Test import java.util.UUID import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit +@Ignore class PromotionsIT { object Conf { @@ -55,7 +57,7 @@ class PromotionsIT { } @Test - fun testMultipleProducts() { + fun skipped_testMultipleProducts() { val lock = CountDownLatch(1); var visitorId: UUID? = null var sessionId: UUID? = null @@ -83,7 +85,7 @@ class PromotionsIT { } @Test - fun testOneProduct() { + fun skipped_testOneProduct() { val lock = CountDownLatch(1); var visitorId: UUID? = null var sessionId: UUID? = null @@ -111,7 +113,7 @@ class PromotionsIT { } @Test - fun testNoProduct() { + fun skipped_testNoProduct() { val lock = CountDownLatch(1); var visitorId: UUID? = null var sessionId: UUID? = null