Skip to content

Commit

Permalink
Tests + refactoring Moments API
Browse files Browse the repository at this point in the history
  • Loading branch information
tamayok committed Apr 24, 2024
1 parent e253b4a commit 9a1e29c
Show file tree
Hide file tree
Showing 7 changed files with 257 additions and 96 deletions.
10 changes: 5 additions & 5 deletions mobile/src/main/java/com/tealium/mobile/TealiumHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ object TealiumHelper : ActivityDataCollector {
val config = TealiumConfig(
application,
"tealiummobile",
"android",
"demo",
Environment.DEV,
modules = mutableSetOf(
Modules.Lifecycle,
Expand Down Expand Up @@ -88,7 +88,7 @@ object TealiumHelper : ActivityDataCollector {
// overrideConsentCategoriesKey = "my_consent_categories_key"

visitorIdentityKey = BuildConfig.IDENTITY_KEY
momentsApiRegion = MomentsApiRegion.OREGON
momentsApiRegion = MomentsApiRegion.US_EAST
}

Tealium.create(BuildConfig.TEALIUM_INSTANCE, config) {
Expand Down Expand Up @@ -162,8 +162,8 @@ object TealiumHelper : ActivityDataCollector {
}

fun getMomentsVisitorData() {
Tealium[BuildConfig.TEALIUM_INSTANCE]?.momentsApi?.requestVisitorDataForEngine(
"123",
Tealium[BuildConfig.TEALIUM_INSTANCE]?.momentsApi?.fetchEngineResponse(
"4625fd31-cd87-444e-9470-7467f2e963ba",
object : ResponseListener<EngineResponse> {
override fun success(data: EngineResponse) {
Logger.dev(BuildConfig.TAG, "Visitor data badges: ${data.badges.toString()}")
Expand All @@ -172,7 +172,7 @@ object TealiumHelper : ActivityDataCollector {
}

override fun failure(errorCode: ErrorCode, message: String) {
Logger.dev(BuildConfig.TAG, "Moments API Error - ${errorCode}: $message")
Logger.dev(BuildConfig.TAG, "Moments API Error - ${errorCode.value}: $message")
}
}
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
package com.tealium.visitorservice.momentsapi

import com.tealium.core.LogLevel
import java.util.Locale

enum class ErrorCode(val value: Int) {
BAD_REQUEST(400 ),
ENGINE_NOT_ENABLED(403),
VISITOR_NOT_FOUND(404),
NOT_CONNECTED (0),
INVALID_JSON (1),
UNKNOWN_ERROR(2)
UNKNOWN_ERROR(2);

companion object {
fun fromInt(int: Int) : ErrorCode {
return when(int) {
400 -> BAD_REQUEST
403 -> ENGINE_NOT_ENABLED
404 -> VISITOR_NOT_FOUND
else -> UNKNOWN_ERROR
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class HttpClient(private val context: Context) : NetworkClient {
val response = reader.readText()
listener.success(response)
}
else -> listener.failure(ErrorCode.valueOf(responseCode.toString()), responseMessage)
else -> listener.failure(ErrorCode.fromInt(responseCode), responseMessage)
}
} catch (ex: Exception) {
listener.failure(ErrorCode.UNKNOWN_ERROR, ex.message ?: "Unknown error fetching engine response")
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,70 @@ import com.tealium.core.Modules
import com.tealium.core.Tealium
import com.tealium.core.TealiumContext
import com.tealium.visitorservice.BuildConfig
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.json.JSONObject
import java.lang.Exception
import java.net.URL

class MomentsApiService @JvmOverloads constructor(
private val context: TealiumContext,
private val momentsApiManager: MomentsApiManager = MomentsManager(context)
private val networkClient: NetworkClient = HttpClient(context.config.application),
private val backgroundScope: CoroutineScope = CoroutineScope(Dispatchers.IO)
) : Module {

override var enabled: Boolean = true // change
override val name: String = MODULE_NAME

fun requestVisitorDataForEngine(engineId: String, responseListener: ResponseListener<EngineResponse>) {
momentsApiManager.fetchEngineResponse(engineId, responseListener)
fun fetchEngineResponse(engineId: String, responseListener: ResponseListener<EngineResponse>) {
fetchResponse(engineId, responseListener)
}

private fun fetchResponse(engineId: String, handler: ResponseListener<EngineResponse>) {
backgroundScope.launch {
val urlString = generateMomentsApiUrl(engineId)
networkClient.get(URL(urlString), object : ResponseListener<String> {
override fun success(data: String) {
try {
val json = JSONObject(data)
val visitorData = EngineResponse.fromJson(json)
handler.success(visitorData)
} catch (ex: Exception) {
handler.failure(ErrorCode.INVALID_JSON, "Invalid JSON EngineResponse")
}
}

override fun failure(errorCode: ErrorCode, message: String) {
handler.failure(errorCode, message)
}
})
}
}

internal fun generateMomentsApiUrl(engineId: String): String {
return DEFAULT_VISITOR_SERVICE_TEMPLATE
.replace(PLACEHOLDER_ACCOUNT, context.config.accountName)
.replace(PLACEHOLDER_REGION, context.config.momentsApiRegion!!.value)
.replace(PLACEHOLDER_ACCOUNT, context.config.accountName)
.replace(PLACEHOLDER_PROFILE, context.config.profileName)
.replace(PLACEHOLDER_ENGINE_ID, engineId)
.replace(PLACEHOLDER_VISITOR_ID, context.visitorId)
}

companion object : ModuleFactory {
const val MODULE_NAME = "MomentsApi"
const val MODULE_VERSION = BuildConfig.LIBRARY_VERSION

const val PLACEHOLDER_REGION = "{{region}}"
const val PLACEHOLDER_ACCOUNT = "{{account}}"
const val PLACEHOLDER_PROFILE = "{{profile}}"
const val PLACEHOLDER_ENGINE_ID = "{{engineId}}"
const val PLACEHOLDER_VISITOR_ID = "{{visitorId}}"

const val DEFAULT_VISITOR_SERVICE_TEMPLATE =
"https://personalization-api.$PLACEHOLDER_REGION.prod.tealiumapis.com/personalization/accounts/$PLACEHOLDER_ACCOUNT/profiles/$PLACEHOLDER_PROFILE/engines/$PLACEHOLDER_ENGINE_ID/visitors/$PLACEHOLDER_VISITOR_ID?ignoreTapid=true"

override fun create(context: TealiumContext): Module {
context.config.momentsApiRegion?.let {
return MomentsApiService(context)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.tealium.visitorservice.momentsapi

import org.json.JSONObject
import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
class EngineResponseTest {
val validEngineResponseString = """
{
"audiences": [
"audience_1",
"audience_2",
"audience_3"
],
"badges": [
"13"
],
"properties": {
"45": "Android",
"46": "Android",
"47": "mobile application",
"5135": "blue_shoes"
}
}
"""

@Test
fun engineResponseDecodeAudiencesSuccess() {
val json = JSONObject(validEngineResponseString)
val engineResponse = EngineResponse.fromJson(json)

assertEquals(engineResponse.audiences?.get(0), "audience_1")
assertEquals(engineResponse.audiences?.get(1), "audience_2")
assertEquals(engineResponse.audiences?.get(2), "audience_3")
}

@Test
fun engineResponseDecodeNullAudience() {
val json = JSONObject(validEngineResponseString)
val engineResponse = EngineResponse.fromJson(json)

engineResponse.audiences?.contains("audience_4")?.let { assertFalse(it) }
}

@Test
fun engineResponseDecodeBadgesSuccess() {
val json = JSONObject(validEngineResponseString)
val engineResponse = EngineResponse.fromJson(json)

engineResponse.badges?.contains("13")?.let { assertTrue(it) }
}

@Test
fun engineResponseDecodeNullBadge() {
val json = JSONObject(validEngineResponseString)
val engineResponse = EngineResponse.fromJson(json)

engineResponse.badges?.contains("33")?.let { assertFalse(it) }
}

@Test
fun engineResponseDecodePropertiesSuccess() {
val json = JSONObject(validEngineResponseString)
val engineResponse = EngineResponse.fromJson(json)

assertEquals(engineResponse.properties?.getValue("45"), "Android")
assertEquals(engineResponse.properties?.getValue("46"), "Android")
assertEquals(engineResponse.properties?.getValue("47"), "mobile application")
assertEquals(engineResponse.properties?.getValue("5135"), "blue_shoes")
}

@Test
fun engineResponseDecodeNullProperty() {
val json = JSONObject(validEngineResponseString)
val engineResponse = EngineResponse.fromJson(json)

assertNull(engineResponse.properties?.get("333"))
}
}
Loading

0 comments on commit 9a1e29c

Please sign in to comment.