Skip to content

Commit

Permalink
[DIA-2654] Flush data if authId or propertyId changes (#711)
Browse files Browse the repository at this point in the history
  • Loading branch information
bohdan-go-wombat authored Nov 1, 2023
1 parent 9c908c5 commit 099e567
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ internal interface CampaignManager {
var choiceResp: ChoiceResp?
var dataRecordedConsent: Instant?
var authId: String?
var propertyId: Int

fun handleAuthIdOrPropertyIdChange(newAuthId: String?, newPropertyId: Int)
fun handleMetaDataResponse(response: MetaDataResp?)
fun handleOldLocalData()
fun getGdprPvDataBody(messageReq: MessagesParamReq): JsonObject
Expand Down Expand Up @@ -364,6 +366,12 @@ private class CampaignManagerImpl(
dataStorage.saveAuthId(value)
}

override var propertyId: Int
get() = dataStorage.propertyId
set(value) {
dataStorage.propertyId = value
}

// Optimized Implementation below

val isNewUser: Boolean
Expand Down Expand Up @@ -413,22 +421,23 @@ private class CampaignManagerImpl(
val hasNonEligibleLocalDataVersion =
dataStorage.localDataVersion != DataStorage.LOCAL_DATA_VERSION_HARDCODED_VALUE

val res = (isGdprOrCcpaUuidPresent && isLocalStateEmpty) ||
isV6LocalStatePresent ||
isV6LocalStatePresent2 ||
hasNonEligibleLocalDataVersion
val isEligibleForCallingConsentStatus =
(isGdprOrCcpaUuidPresent && isLocalStateEmpty) ||
isV6LocalStatePresent ||
isV6LocalStatePresent2 ||
hasNonEligibleLocalDataVersion

logger?.computation(
tag = "shouldCallConsentStatus",
msg = """
isGdprOrCcpaUuidPresent[$isGdprOrCcpaUuidPresent] - isLocalStateEmpty[$isLocalStateEmpty]
isV6LocalStatePresent[$isV6LocalStatePresent] - isV6LocalStatePresent2[$isV6LocalStatePresent2]
hasNonEligibleLocalDataVersion[$hasNonEligibleLocalDataVersion]
res[$res]
isEligibleForCallingConsentStatus[$isEligibleForCallingConsentStatus]
""".trimIndent()
)

return res
return isEligibleForCallingConsentStatus
}

override var gdprMessageMetaData: MessageMetaData?
Expand Down Expand Up @@ -523,6 +532,27 @@ private class CampaignManagerImpl(
override val hasLocalData: Boolean
get() = dataStorage.gdprConsentStatus != null || dataStorage.ccpaConsentStatus != null

/**
* The method that checks if the authId or propertyId was changed, if so it will flush all the
* data. At the end, it will update the authId and propertyId with the corresponding values.
*/
override fun handleAuthIdOrPropertyIdChange(
newAuthId: String?,
newPropertyId: Int,
) {
// flush local data if proper authId or propertyId change was detected
val isNewAuthId = newAuthId != null && newAuthId != authId
val isNewPropertyId = newPropertyId != propertyId
val hasPreviousPropertyId = propertyId != 0
if (isNewAuthId || isNewPropertyId && hasPreviousPropertyId) {
dataStorage.clearAll()
}

// update stored values of authId and propertyId
authId = newAuthId
propertyId = newPropertyId
}

override fun handleMetaDataResponse(response: MetaDataResp?) {
// update meta data response in the data storage
metaDataResp = response
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,14 +135,19 @@ private class ServiceImpl(
onFailure: (Throwable, Boolean) -> Unit,
) {
execManager.executeOnWorkerThread {
campaignManager.authId = messageReq.authId

if (connectionManager.isConnected.not()) {
val noInternetConnectionException = NoInternetConnectionException()
onFailure(noInternetConnectionException, true)
return@executeOnWorkerThread
}

// check whether the authId or propertyId changed, and handle the flow accordingly
campaignManager.handleAuthIdOrPropertyIdChange(
newAuthId = messageReq.authId,
newPropertyId = spConfig.propertyId,
)

val metadataResponse = this.getMetaData(messageReq.toMetaDataParamReq(campaigns4Config))
.executeOnLeft { metaDataError ->
onFailure(metaDataError, true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ internal interface DataStorage : DataStorageGdpr, DataStorageCcpa {
const val CHOICE_RESP = "sp.key.choice"
const val DATA_RECORDED_CONSENT = "sp.key.data.recorded.consent"

const val KEY_PROPERTY_ID = "sp.key.config.propertyId"

const val CONSENT_STATUS_RESPONSE = "sp.key.consent.status.response"
const val GDPR_CONSENT_STATUS = "sp.gdpr.key.consent.status"
const val CCPA_CONSENT_STATUS = "sp.ccpa.key.consent.status"
Expand All @@ -41,6 +43,7 @@ internal interface DataStorage : DataStorageGdpr, DataStorageCcpa {
var ccpaConsentStatus: String?
var messagesOptimizedLocalState: String?
var nonKeyedLocalState: String?
var propertyId: Int

fun saveLocalState(value: String)
fun getLocalState(): String?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.sourcepoint.cmplibrary.data.local.DataStorage.Companion.CONSENT_STATU
import com.sourcepoint.cmplibrary.data.local.DataStorage.Companion.CONSENT_STATUS_RESPONSE
import com.sourcepoint.cmplibrary.data.local.DataStorage.Companion.DATA_RECORDED_CONSENT
import com.sourcepoint.cmplibrary.data.local.DataStorage.Companion.GDPR_CONSENT_STATUS
import com.sourcepoint.cmplibrary.data.local.DataStorage.Companion.KEY_PROPERTY_ID
import com.sourcepoint.cmplibrary.data.local.DataStorage.Companion.LOCAL_DATA_VERSION_HARDCODED_VALUE
import com.sourcepoint.cmplibrary.data.local.DataStorage.Companion.LOCAL_DATA_VERSION_KEY
import com.sourcepoint.cmplibrary.data.local.DataStorage.Companion.LOCAL_STATE
Expand Down Expand Up @@ -138,6 +139,12 @@ private class DataStorageImpl(
.putString(NON_KEYED_LOCAL_STATE, value)
.apply()
}
override var propertyId: Int
get() = preference.getInt(KEY_PROPERTY_ID, 0)
set(value) = preference
.edit()
.putInt(KEY_PROPERTY_ID, value)
.apply()

override var consentStatus: String?
get() = preference.getString(CONSENT_STATUS, null)
Expand Down Expand Up @@ -210,6 +217,7 @@ private class DataStorageImpl(
.remove(CCPA_CONSENT_STATUS)
.remove(MESSAGES_OPTIMIZED_LOCAL_STATE)
.remove(NON_KEYED_LOCAL_STATE)
.remove(KEY_PROPERTY_ID)
.apply()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ class MainActivityKotlinOldConsentTest {
@Test
fun GIVEN_an_old_CCPA_GDPR_v6LocalState_VERIFY_that_the_migration_is_performed() = runBlocking<Unit> {

val v6LocalState = JSONObject(TestData.storedConsentGdprCcap)
val v6LocalState = JSONObject(TestData.storedConsentGdprCcpa)

val spClient = mockk<SpClient>(relaxed = true)

Expand Down Expand Up @@ -222,7 +222,7 @@ class MainActivityKotlinOldConsentTest {
@Test
fun GIVEN_an_old_CCPAv6LocalState_VERIFY_that_the_migration_is_performed() = runBlocking<Unit> {

val v6LocalState = JSONObject(TestData.storedConsentCcap)
val v6LocalState = JSONObject(TestData.storedConsentCcpa)

val spClient = mockk<SpClient>(relaxed = true)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ import com.sourcepoint.app.v6.TestUseCase.Companion.clickOnRefreshBtnActivity
import com.sourcepoint.app.v6.TestUseCase.Companion.mockModule
import com.sourcepoint.app.v6.TestUseCase.Companion.tapAcceptAllOnWebView
import com.sourcepoint.app.v6.TestUseCase.Companion.tapAcceptCcpaOnWebView
import com.sourcepoint.app.v6.TestUseCase.Companion.tapAcceptOnOk
import com.sourcepoint.app.v6.TestUseCase.Companion.tapAcceptOnWebView
import com.sourcepoint.app.v6.TestUseCase.Companion.tapAcceptOnWebViewDE
import com.sourcepoint.app.v6.TestUseCase.Companion.tapCancelOnWebView
import com.sourcepoint.app.v6.TestUseCase.Companion.tapFeaturesOnWebView
import com.sourcepoint.app.v6.TestUseCase.Companion.tapNetworkOnWebView
Expand Down Expand Up @@ -59,6 +57,7 @@ import org.junit.After
import org.junit.Test
import org.junit.runner.RunWith
import org.koin.core.context.loadKoinModules
import java.util.UUID

@RunWith(AndroidJUnit4ClassRunner::class)
class MainActivityKotlinTest {
Expand Down Expand Up @@ -1076,6 +1075,86 @@ class MainActivityKotlinTest {
}
}

/**
* UI test that verifies that there was a clean up of local data after the user changed their
* auth ID. Due to the fact that the clean up happens, the message flow appears again, the user
* make their choice and the consent UUIDs of both campaigns changes.
*/
@Test
fun given_the_user_has_consent_and_the_auth_id_changes_then_should_flush_data() = runBlocking<Unit> {

val storedConsent = JSONObject(TestData.storedConsentWithAuthIdAndPropertyIdV741)
val spClient = mockk<SpClient>(relaxed = true)
val newAuthId = UUID.randomUUID().toString()

loadKoinModules(
mockModule(
spConfig = spConf,
gdprPmId = "488393",
ccpaPmId = "509688",
spClientObserver = listOf(spClient),
diagnostic = storedConsent.toList(),
pAuthId = newAuthId
)
)

scenario = launchActivity()

wr { tapAcceptAllOnWebView() }
wr { tapAcceptAllOnWebView() }

wr { verify(exactly = 0) { spClient.onError(any()) } }
wr { verify(exactly = 2) { spClient.onUIReady(any()) } }
wr { verify(exactly = 1) { spClient.onSpFinished(any()) } }

scenario.onActivity { activity ->
PreferenceManager.getDefaultSharedPreferences(activity).run {
getString("sp.gdpr.authId", null).assertEquals(newAuthId)
}
}
}

/**
* UI test that verifies that there was a clean up of local data after the user changed their
* property ID. Due to the fact that the clean up happens, the message flow appears again, the user
* make their choice and the consent UUIDs of both campaigns changes.
*/
@Test
fun given_the_user_has_consent_and_the_property_id_changes_then_should_flush_data() = runBlocking<Unit> {

val storedConsent = JSONObject(TestData.storedConsentWithAuthIdAndPropertyIdV741)
val spClient = mockk<SpClient>(relaxed = true)
val storedPropertyId = 31226
val newPropertyId = 16893

loadKoinModules(
mockModule(
spConfig = spConf,
gdprPmId = "488393",
ccpaPmId = "509688",
spClientObserver = listOf(spClient),
diagnostic = storedConsent.toList() + listOf(
Pair("sp.key.config.propertyId", storedPropertyId)
),
)
)

scenario = launchActivity()

wr { tapAcceptAllOnWebView() }
wr { tapAcceptAllOnWebView() }

wr { verify(exactly = 0) { spClient.onError(any()) } }
wr { verify(exactly = 2) { spClient.onUIReady(any()) } }
wr { verify(exactly = 1) { spClient.onSpFinished(any()) } }

scenario.onActivity { activity ->
PreferenceManager.getDefaultSharedPreferences(activity).run {
getInt("sp.key.config.propertyId", 0).assertEquals(newPropertyId)
}
}
}

private fun <E> check(block: () -> E): E? {
return try {
block.invoke()
Expand Down
Loading

0 comments on commit 099e567

Please sign in to comment.