Skip to content

Commit

Permalink
feat: added some fields to the message_xxx event. (#210)
Browse files Browse the repository at this point in the history
feat: added some fields to the message_xxx event.
  • Loading branch information
tomoponzoo authored Feb 14, 2023
1 parent 31367d6 commit 975d6a9
Show file tree
Hide file tree
Showing 11 changed files with 416 additions and 38 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
| core | イベントトラッキング機能を提供します。 | 2.21.0 |
| inappmessaging | アプリ内メッセージ機能を提供します。 | 2.14.0 |
| notifications | プッシュ通知の受信および効果測定機能を提供します。 | 2.9.1 |
| variables | 設定値配信機能を提供します。 | 2.3.1 |
| variables | 設定値配信機能を提供します。 | 2.4.0 |
| visualtracking | ビジュアルトラッキング機能を提供します。| 2.7.1 |
| Karte Gradle Plugin | ビジュアルトラッキング機能に必要なプラグインです。| 2.5.0 |

Expand All @@ -27,6 +27,10 @@
** 🔨CHANGED**
- identifyイベントのuser_idに明示的に空文字が指定された場合に警告を出力するように変更しました。

### Variables 2.4.0
** 🔨CHANGED**
- 効果測定用のイベントにフィールドを追加しました。

# Releases - 2022.09.09

### Notifications 2.9.1
Expand Down
23 changes: 22 additions & 1 deletion core/src/main/java/io/karte/android/tracking/DTO.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,25 @@ interface DTO<T> {
* @property[shortenId] shorten_id フィールド
* @property[type] type フィールド
* @property[content] content フィールド
* @property[noAction] no_action フィールド
* @property[reason] reason フィールド
* @property[responseTimestamp] response_timestamp フィールド
*/
data class Action<T : DTO<T>>(
var shortenId: String? = null,
var type: String? = null,
var content: T? = null
var content: T? = null,
var noAction: Boolean? = null,
var reason: String? = null,
var responseTimestamp: String? = null
) : DTO<Action<T>> {
override fun load(jsonObject: JSONObject?): Action<T> = apply {
shortenId = jsonObject?.optString("shorten_id")
type = jsonObject?.optString("type")
content = content?.load(jsonObject?.optJSONObject("content"))
noAction = jsonObject?.optBoolean("no_action")
reason = jsonObject?.optString("reason")
responseTimestamp = jsonObject?.optString("response_timestamp")
}
}

Expand All @@ -56,3 +65,15 @@ data class Campaign(
serviceActionType = jsonObject?.optString("service_action_type")
}
}

/**
* アクションのトリガー情報を保持するデータクラスです。
* @property[eventHashes] event_hashes フィールド
*/
data class Trigger(
var eventHashes: String? = null
) : DTO<Trigger> {
override fun load(jsonObject: JSONObject?): Trigger = apply {
eventHashes = jsonObject?.optString("event_hashes")
}
}
9 changes: 8 additions & 1 deletion core/src/main/java/io/karte/android/tracking/Event.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package io.karte.android.tracking

import io.karte.android.utilities.format
import io.karte.android.utilities.merge
import io.karte.android.utilities.toMap
import io.karte.android.utilities.toValues
import org.json.JSONObject
Expand Down Expand Up @@ -219,7 +220,13 @@ class MessageEvent(
val shortenId: String,
values: Values? = null
) : Event(CustomEventName(type.eventNameStr), valuesOf(values) {
this["message"] = mapOf("campaign_id" to campaignId, "shorten_id" to shortenId)
val merged = merge(mapOf(
"message" to mapOf(
"campaign_id" to campaignId,
"shorten_id" to shortenId
)
))
putAll(merged)
})

/**各イベント名を示すインターフェースです。*/
Expand Down
19 changes: 19 additions & 0 deletions core/src/main/java/io/karte/android/utilities/Extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,22 @@ fun JSONObject.toValues(): Values {
fun Values.format(): Values {
return mapValues { it.value.format() }.filterNotNull()
}

fun Values.merge(other: Values): Values {
return mergeInternal(toMutableMap(), other.toMap())
}

private fun mergeInternal(base: MutableMap<String, Any>, additional: Map<String, Any>): Map<String, Any> {
for ((k, av) in additional) {
val tv = base[k]
if (tv == null) {
base[k] = av
} else if (tv is Map<*, *> && av is Map<*, *>) {
@Suppress("UNCHECKED_CAST")
base[k] = mergeInternal((tv as Map<String, Any>).toMutableMap(), av as Map<String, Any>)
} else {
base[k] = av
}
}
return base
}
21 changes: 19 additions & 2 deletions core/src/test/java/io/karte/android/TestUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -121,18 +121,35 @@ fun createMessage(
campaignId: String = "sample_campaign",
shortenId: String = "sample_shorten",
content: JSONObject = JSONObject(),
pluginType: String = "webpopup"
pluginType: String = "webpopup",
responseTimestamp: String = "sample_response_timestamp",
noAction: Boolean? = null,
reason: String? = null,
triggerEventHash: String = "sample_trigger_event_hash"
): JSONObject {
val action = JSONObject()
.put("campaign_id", campaignId)
.put("shorten_id", shortenId)
.put("plugin_type", pluginType)
.put("content", content)
.put("response_timestamp", responseTimestamp)
noAction?.let {
action.put("no_action", it)
}
reason?.let {
action.put("reason", it)
}

val campaign = JSONObject()
.put("_id", campaignId)
.put("service_action_type", pluginType)
val trigger = JSONObject()
.put("event_hashes", triggerEventHash)

return JSONObject().put("action", action).put("campaign", campaign)
return JSONObject()
.put("action", action)
.put("campaign", campaign)
.put("trigger", trigger)
}

fun createControlGroupMessage(
Expand Down
62 changes: 62 additions & 0 deletions core/src/test/java/io/karte/android/unit/UtilitiesTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
package io.karte.android.unit

import io.karte.android.shadow.CustomShadowWebView
import io.karte.android.tracking.valuesOf
import io.karte.android.utilities.isAscii
import io.karte.android.utilities.merge
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
Expand Down Expand Up @@ -46,4 +48,64 @@ class UtilitiesTest {
Assert.assertTrue(" ".isAscii())
Assert.assertTrue("_".isAscii())
}

@Test
fun merge() {
val baseString = """
{
"f1": {
"f1a": {
"f1a1": "f1a1v",
"f1a3": "f1a3v"
}
},
"f2": "f2v",
"f4": {
"f4a": "f4av"
}
}
""".trimIndent()
val additionalString = """
{
"f1": {
"f1a": {
"f1a2": "f1a2v",
"f1a3": "f1a3v2"
}
},
"f3": "f3v",
"f4": "f4v"
}
""".trimIndent()
val base = valuesOf(baseString)
val additional = valuesOf(additionalString)
val merged = base.merge(additional)

Assert.assertEquals(value(merged, "f1.f1a.f1a1"), "f1a1v")
Assert.assertEquals(value(merged, "f1.f1a.f1a2"), "f1a2v")
Assert.assertEquals(value(merged, "f1.f1a.f1a3"), "f1a3v2")
Assert.assertEquals(value(merged, "f2"), "f2v")
Assert.assertEquals(value(merged, "f3"), "f3v")
Assert.assertEquals(value(merged, "f4"), "f4v")

Assert.assertNull(value(base, "f1.f1a.f1a2"))
Assert.assertNull(value(base, "f3"))
}

private fun value(values: Map<String, Any>, path: String): Any? {
val components = path.split(".", ignoreCase = false, limit = 2)
return when (components.size) {
0 -> null
1 -> values[components[0]]
else -> {
val v = values[components[0]]
if (v is Map<*, *>) {
@Suppress("UNCHECKED_CAST")
value(v as Map<String, Any>, components[1])
} else {
null
}
}
}
}
}
12 changes: 10 additions & 2 deletions variables/src/main/java/io/karte/android/variables/Variable.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ private const val LOG_TAG = "Karte.Variable"
private const val JSON_KEY_VALUE = "value"
private const val JSON_KEY_CAMPAIGN_ID = "campaign_id"
private const val JSON_KEY_SHORTEN_ID = "shorten_id"
private const val JSON_KEY_TIMESTAMP = "timestamp"
private const val JSON_KEY_EVENT_HASH = "event_hash"

/**
* 設定値とそれに付随する情報を保持するためのクラスです。
Expand All @@ -45,7 +47,9 @@ data class Variable internal constructor(
val name: String,
val campaignId: String? = null,
val shortenId: String? = null,
val value: Any? = null
val value: Any? = null,
val timestamp: String? = null,
val eventHash: String? = null
) {

/**
Expand Down Expand Up @@ -165,6 +169,8 @@ data class Variable internal constructor(
.put(JSON_KEY_CAMPAIGN_ID, campaignId)
.put(JSON_KEY_SHORTEN_ID, shortenId)
.put(JSON_KEY_VALUE, value)
.put(JSON_KEY_TIMESTAMP, timestamp)
.put(JSON_KEY_EVENT_HASH, eventHash)
.toString()
} catch (e: JSONException) {
null
Expand All @@ -180,7 +186,9 @@ data class Variable internal constructor(
key,
json.getString(JSON_KEY_CAMPAIGN_ID),
json.getString(JSON_KEY_SHORTEN_ID),
json.getString(JSON_KEY_VALUE)
json.getString(JSON_KEY_VALUE),
if (json.has(JSON_KEY_TIMESTAMP)) json.getString(JSON_KEY_TIMESTAMP) else null,
if (json.has(JSON_KEY_EVENT_HASH)) json.getString(JSON_KEY_EVENT_HASH) else null
)
} catch (e: JSONException) {
Logger.e(LOG_TAG, "Failed to load saved variable:", e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package io.karte.android.variables.internal
import io.karte.android.tracking.Action
import io.karte.android.tracking.Campaign
import io.karte.android.tracking.DTO
import io.karte.android.tracking.Trigger
import org.json.JSONObject

internal fun parse(jsonObject: JSONObject): VariableMessage? {
Expand Down Expand Up @@ -48,7 +49,8 @@ internal data class Content(var inlinedVariables: List<InlinedVariable> = emptyL

internal data class VariableMessage(
val action: Action<Content> = Action(content = Content()),
var campaign: Campaign = Campaign()
var campaign: Campaign = Campaign(),
var trigger: Trigger = Trigger()
) : DTO<VariableMessage> {
val isEnabled: Boolean
get() = campaign.campaignId != null && action.shortenId != null &&
Expand All @@ -59,5 +61,6 @@ internal data class VariableMessage(
override fun load(jsonObject: JSONObject?): VariableMessage = apply {
action.load(jsonObject?.optJSONObject("action"))
campaign.load(jsonObject?.optJSONObject("campaign"))
trigger.load(jsonObject?.optJSONObject("trigger"))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import io.karte.android.tracking.Tracker
import io.karte.android.tracking.Values
import io.karte.android.tracking.client.TrackRequest
import io.karte.android.tracking.client.TrackResponse
import io.karte.android.utilities.merge
import io.karte.android.utilities.toValues
import io.karte.android.variables.BuildConfig
import io.karte.android.variables.FetchCompletion
Expand Down Expand Up @@ -95,15 +96,63 @@ internal class VariablesService : Library, ActionModule, UserModule {
variable.shortenId ?: return@forEach
if (alreadySentCampaignIds.contains(variable.campaignId)) return@forEach
alreadySentCampaignIds.add(variable.campaignId)
Tracker.track(
MessageEvent(
type,
variable.campaignId,
variable.shortenId,
values
)
track(
type,
variable.campaignId,
variable.shortenId,
values,
variable.timestamp,
variable.eventHash
)
}
}

private fun track(type: MessageEventType, campaignId: String, shortenId: String, values: Values?, timestamp: String?, eventHash: String?) {
var base = (values ?: mapOf()).merge(mapOf(
"message" to mapOf(
"frequency_type" to "access"
)
))

timestamp?.let {
base = base.merge(mapOf(
"message" to mapOf(
"response_id" to "${it}_$shortenId",
"response_timestamp" to it
)
))
}

eventHash?.let {
base = base.merge(mapOf(
"message" to mapOf(
"trigger" to mapOf(
"event_hashes" to eventHash
)
)
))
}

Tracker.track(MessageEvent(type, campaignId, shortenId, base))
}
private fun trackReady(message: VariableMessage) {
val campaignId = message.campaign.campaignId ?: return
val shortenId = message.action.shortenId ?: return

val noAction = message.action.noAction ?: false
val values = mutableMapOf<String, Any>("no_action" to noAction)
if (noAction) {
message.action.reason?.let { values["reason"] = it }
}

track(
MessageEventType.Ready,
campaignId,
shortenId,
values,
message.action.responseTimestamp,
message.trigger.eventHashes
)
}

//region Libraary
Expand Down Expand Up @@ -136,25 +185,22 @@ internal class VariablesService : Library, ActionModule, UserModule {
messages.forEach messages@{ message ->
val shortenId = message.action.shortenId?.let { it } ?: return@messages
val campaignId = message.campaign.campaignId?.let { it } ?: return@messages

if (!message.isControlGroup) {
message.action.content?.inlinedVariables?.forEach variables@{ inlinedVariable ->
val name = inlinedVariable.name ?: return@variables
val value = inlinedVariable.value ?: return@variables
val timestamp = message.action.responseTimestamp
val eventHash = message.trigger.eventHashes
Logger.d(
LOG_TAG,
"Write variable: $name. campaignId=$campaignId, shortenId=$shortenId"
)
repository.put(name, Variable(name, campaignId, shortenId, value).serialize())
repository.put(name, Variable(name, campaignId, shortenId, value, timestamp, eventHash).serialize())
}
}

Tracker.track(
MessageEvent(
MessageEventType.Ready,
campaignId,
shortenId
)
)
trackReady(message)
}
}

Expand Down
Loading

0 comments on commit 975d6a9

Please sign in to comment.