From 3a877a3a0a857615d02f4e6733773adf93f0d473 Mon Sep 17 00:00:00 2001 From: Aitor Viana Date: Thu, 14 Nov 2024 08:51:11 +0000 Subject: [PATCH] Support settings for top-level and sub-feature and remove sub-feature config (#5269) Task/Issue URL: https://app.asana.com/0/1198194956794324/1208740919623939/f ### Description We want to support `settings` for all top-level and sub-features because we can define schemas and validate them in the remote config As a result we can remove the sub-feature `config` ### Steps to test this PR _Test_ - [ ] test pass - [ ] smoke test abn - [ ] smoke test autofill --------- Co-authored-by: Marcos Co-authored-by: Craig Russell --- .../annotations/ContributesRemoteFeature.kt | 1 + .../ContributesRemoteFeatureCodeGenerator.kt | 14 +- .../trackerdetection/blocklist/BlockList.kt | 5 +- .../BlockListInterceptorApiPlugin.kt | 23 ++- .../brokensite/api/BrokenSiteSubmitterTest.kt | 17 +- .../refreshpixels/RefreshPixelSenderTest.kt | 17 +- .../BlockListInterceptorApiPluginTest.kt | 43 ++--- .../BlockListPrivacyTogglePluginTest.kt | 17 +- autofill/autofill-impl/build.gradle | 1 + .../feature/AutofillImportPasswordSettings.kt | 30 ++- ...tofillImportPasswordConfigStoreImplTest.kt | 33 +++- common/common-test/build.gradle | 1 + .../common/test/json/JSONObjectAdapter.kt | 49 +++++ .../feature/toggles/api/FeatureSettings.kt | 2 + .../feature/toggles/api/FeatureToggles.kt | 8 +- .../feature-toggles-impl/lint-baseline.xml | 177 ++++++++---------- ...ntributesRemoteFeatureCodeGeneratorTest.kt | 63 +++++-- 17 files changed, 315 insertions(+), 186 deletions(-) create mode 100644 common/common-test/src/main/java/com/duckduckgo/common/test/json/JSONObjectAdapter.kt diff --git a/anvil/anvil-annotations/src/main/java/com/duckduckgo/anvil/annotations/ContributesRemoteFeature.kt b/anvil/anvil-annotations/src/main/java/com/duckduckgo/anvil/annotations/ContributesRemoteFeature.kt index 4ca05267ffb3..bd5205973c98 100644 --- a/anvil/anvil-annotations/src/main/java/com/duckduckgo/anvil/annotations/ContributesRemoteFeature.kt +++ b/anvil/anvil-annotations/src/main/java/com/duckduckgo/anvil/annotations/ContributesRemoteFeature.kt @@ -55,6 +55,7 @@ annotation class ContributesRemoteFeature( val featureName: String, /** The class that implements the [FeatureSettings.Store] interface */ + @Deprecated("Not needed anymore. Settings is now supported in top-level and sub-features and Toggle#getSettings returns it") val settingsStore: KClass<*> = Unit::class, /** The class that implements the [FeatureExceptions.Store] interface */ diff --git a/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesRemoteFeatureCodeGenerator.kt b/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesRemoteFeatureCodeGenerator.kt index 24816de89729..063c7edfcc13 100644 --- a/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesRemoteFeatureCodeGenerator.kt +++ b/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesRemoteFeatureCodeGenerator.kt @@ -498,6 +498,7 @@ class ContributesRemoteFeatureCodeGenerator : CodeGenerator { minSupportedVersion = feature.minSupportedVersion, targets = emptyList(), cohorts = emptyList(), + settings = feature.settings?.toString(), ) ) @@ -528,7 +529,7 @@ class ContributesRemoteFeatureCodeGenerator : CodeGenerator { weight = cohort.weight, ) } ?: emptyList() - val config = jsonToggle?.config ?: emptyMap() + val settings = jsonToggle?.settings?.toString() this.feature.get().invokeMethod(subfeature.key).setRawStoredState( Toggle.State( remoteEnableState = newStateValue, @@ -539,7 +540,7 @@ class ContributesRemoteFeatureCodeGenerator : CodeGenerator { assignedCohort = previousAssignedCohort, targets = targets, cohorts = cohorts, - config = config, + settings = settings, ), ) } catch(e: Throwable) { @@ -781,10 +782,7 @@ class ContributesRemoteFeatureCodeGenerator : CodeGenerator { "cohorts", List::class.asClassName().parameterizedBy(FqName("JsonToggleCohort").asClassName(module)), ) - .addParameter( - "config", - Map::class.asClassName().parameterizedBy(String::class.asClassName(), String::class.asClassName()), - ) + .addParameter("settings", FqName("org.json.JSONObject").asClassName(module).copy(nullable = true)) .build(), ) .addProperty( @@ -819,8 +817,8 @@ class ContributesRemoteFeatureCodeGenerator : CodeGenerator { ) .addProperty( PropertySpec - .builder("config", Map::class.asClassName().parameterizedBy(String::class.asClassName(), String::class.asClassName())) - .initializer("config") + .builder("settings", FqName("org.json.JSONObject").asClassName(module).copy(nullable = true)) + .initializer("settings") .build(), ) .build() diff --git a/app/src/main/java/com/duckduckgo/app/trackerdetection/blocklist/BlockList.kt b/app/src/main/java/com/duckduckgo/app/trackerdetection/blocklist/BlockList.kt index e98dc1d630f5..62d5aa18e97f 100644 --- a/app/src/main/java/com/duckduckgo/app/trackerdetection/blocklist/BlockList.kt +++ b/app/src/main/java/com/duckduckgo/app/trackerdetection/blocklist/BlockList.kt @@ -70,10 +70,7 @@ interface BlockList { } companion object { - const val EXPERIMENT_PREFIX = "tds" - const val TREATMENT_URL = "treatmentUrl" - const val CONTROL_URL = "controlUrl" - const val NEXT_URL = "nextUrl" + internal const val EXPERIMENT_PREFIX = "tds" } } diff --git a/app/src/main/java/com/duckduckgo/app/trackerdetection/blocklist/BlockListInterceptorApiPlugin.kt b/app/src/main/java/com/duckduckgo/app/trackerdetection/blocklist/BlockListInterceptorApiPlugin.kt index b527a9844ede..5e6ebfda7dad 100644 --- a/app/src/main/java/com/duckduckgo/app/trackerdetection/blocklist/BlockListInterceptorApiPlugin.kt +++ b/app/src/main/java/com/duckduckgo/app/trackerdetection/blocklist/BlockListInterceptorApiPlugin.kt @@ -20,12 +20,12 @@ import com.duckduckgo.app.global.api.ApiInterceptorPlugin import com.duckduckgo.app.trackerdetection.api.TDS_BASE_URL import com.duckduckgo.app.trackerdetection.blocklist.BlockList.Cohorts.CONTROL import com.duckduckgo.app.trackerdetection.blocklist.BlockList.Cohorts.TREATMENT -import com.duckduckgo.app.trackerdetection.blocklist.BlockList.Companion.CONTROL_URL -import com.duckduckgo.app.trackerdetection.blocklist.BlockList.Companion.NEXT_URL -import com.duckduckgo.app.trackerdetection.blocklist.BlockList.Companion.TREATMENT_URL import com.duckduckgo.di.scopes.AppScope import com.duckduckgo.feature.toggles.api.FeatureTogglesInventory import com.squareup.anvil.annotations.ContributesMultibinding +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Moshi +import com.squareup.moshi.Types import javax.inject.Inject import kotlinx.coroutines.runBlocking import okhttp3.Interceptor @@ -38,8 +38,12 @@ import okhttp3.Response ) class BlockListInterceptorApiPlugin @Inject constructor( private val inventory: FeatureTogglesInventory, + private val moshi: Moshi, ) : Interceptor, ApiInterceptorPlugin { + private val jsonAdapter: JsonAdapter> by lazy { + moshi.adapter(Types.newParameterizedType(Map::class.java, String::class.java, String::class.java)) + } override fun intercept(chain: Chain): Response { val request = chain.request().newBuilder() val url = chain.request().url @@ -51,11 +55,16 @@ class BlockListInterceptorApiPlugin @Inject constructor( } return activeExperiment?.let { + val config = activeExperiment.getSettings()?.let { + runCatching { + jsonAdapter.fromJson(it) + }.getOrDefault(emptyMap()) + } ?: emptyMap() val path = when { - activeExperiment.isEnabled(TREATMENT) -> activeExperiment.getConfig()[TREATMENT_URL] - activeExperiment.isEnabled(CONTROL) -> activeExperiment.getConfig()[CONTROL_URL] - else -> activeExperiment.getConfig()[NEXT_URL] - } ?: chain.proceed(request.build()) + activeExperiment.isEnabled(TREATMENT) -> config["treatmentUrl"] + activeExperiment.isEnabled(CONTROL) -> config["controlUrl"] + else -> config["nextUrl"] + } ?: return chain.proceed(request.build()) chain.proceed(request.url("$TDS_BASE_URL$path").build()) } ?: chain.proceed(request.build()) } diff --git a/app/src/test/java/com/duckduckgo/app/brokensite/api/BrokenSiteSubmitterTest.kt b/app/src/test/java/com/duckduckgo/app/brokensite/api/BrokenSiteSubmitterTest.kt index 8159ac10d9ba..e2fb9eb372e8 100644 --- a/app/src/test/java/com/duckduckgo/app/brokensite/api/BrokenSiteSubmitterTest.kt +++ b/app/src/test/java/com/duckduckgo/app/brokensite/api/BrokenSiteSubmitterTest.kt @@ -11,8 +11,6 @@ import com.duckduckgo.app.statistics.pixels.Pixel import com.duckduckgo.app.statistics.pixels.Pixel.PixelType.Count import com.duckduckgo.app.statistics.store.StatisticsDataStore import com.duckduckgo.app.trackerdetection.blocklist.BlockList.Cohorts.TREATMENT -import com.duckduckgo.app.trackerdetection.blocklist.BlockList.Companion.CONTROL_URL -import com.duckduckgo.app.trackerdetection.blocklist.BlockList.Companion.TREATMENT_URL import com.duckduckgo.app.trackerdetection.blocklist.FakeFeatureTogglesInventory import com.duckduckgo.app.trackerdetection.blocklist.TestBlockListFeature import com.duckduckgo.app.trackerdetection.db.TdsMetadataDao @@ -43,6 +41,7 @@ import com.duckduckgo.privacy.config.api.PrivacyConfigData import com.duckduckgo.privacy.config.api.PrivacyFeatureName import com.duckduckgo.privacy.config.api.UnprotectedTemporary import com.duckduckgo.privacyprotectionspopup.api.PrivacyProtectionsPopupExperimentExternalPixels +import com.squareup.moshi.Moshi import java.time.ZoneId import java.time.ZonedDateTime import java.time.temporal.ChronoUnit @@ -68,6 +67,15 @@ class BrokenSiteSubmitterTest { @get:Rule var coroutineRule = CoroutineTestRule() + private val moshi = Moshi.Builder().build() + + private data class Config( + val treatmentUrl: String? = null, + val controlUrl: String? = null, + val nextUrl: String? = null, + ) + private val configAdapter = moshi.adapter(Config::class.java) + private val mockPixel: Pixel = mock() private val mockVariantManager: VariantManager = mock() @@ -605,10 +613,7 @@ class BrokenSiteSubmitterTest { State( remoteEnableState = true, enable = true, - config = mapOf( - TREATMENT_URL to "treatmentUrl", - CONTROL_URL to "controlUrl", - ), + settings = configAdapter.toJson(Config(treatmentUrl = "treatmentUrl", controlUrl = "controlUrl")), assignedCohort = State.Cohort(name = TREATMENT.cohortName, weight = 1, enrollmentDateET = enrollmentDateET), ), ) diff --git a/app/src/test/java/com/duckduckgo/app/browser/refreshpixels/RefreshPixelSenderTest.kt b/app/src/test/java/com/duckduckgo/app/browser/refreshpixels/RefreshPixelSenderTest.kt index 22328321f67c..0d248c24fb00 100644 --- a/app/src/test/java/com/duckduckgo/app/browser/refreshpixels/RefreshPixelSenderTest.kt +++ b/app/src/test/java/com/duckduckgo/app/browser/refreshpixels/RefreshPixelSenderTest.kt @@ -10,8 +10,6 @@ import com.duckduckgo.app.pixels.AppPixelName import com.duckduckgo.app.statistics.pixels.Pixel import com.duckduckgo.app.statistics.pixels.Pixel.PixelType.Daily import com.duckduckgo.app.trackerdetection.blocklist.BlockList.Cohorts.TREATMENT -import com.duckduckgo.app.trackerdetection.blocklist.BlockList.Companion.CONTROL_URL -import com.duckduckgo.app.trackerdetection.blocklist.BlockList.Companion.TREATMENT_URL import com.duckduckgo.app.trackerdetection.blocklist.BlockListPixelsPlugin import com.duckduckgo.app.trackerdetection.blocklist.FakeFeatureTogglesInventory import com.duckduckgo.app.trackerdetection.blocklist.TestBlockListFeature @@ -24,6 +22,7 @@ import com.duckduckgo.feature.toggles.api.FeatureToggles import com.duckduckgo.feature.toggles.api.FeatureTogglesInventory import com.duckduckgo.feature.toggles.api.Toggle.State import com.duckduckgo.feature.toggles.impl.RealFeatureTogglesInventory +import com.squareup.moshi.Moshi import java.time.ZoneId import java.time.ZonedDateTime import java.time.temporal.ChronoUnit @@ -48,6 +47,15 @@ class RefreshPixelSenderTest { @get:Rule var coroutineTestRule = CoroutineTestRule() + private val moshi = Moshi.Builder().build() + + private data class Config( + val treatmentUrl: String? = null, + val controlUrl: String? = null, + val nextUrl: String? = null, + ) + private val configAdapter = moshi.adapter(Config::class.java) + private lateinit var db: AppDatabase private lateinit var refreshDao: RefreshDao private val mockPixel: Pixel = mock() @@ -304,10 +312,7 @@ class RefreshPixelSenderTest { State( remoteEnableState = true, enable = true, - config = mapOf( - TREATMENT_URL to "treatmentUrl", - CONTROL_URL to "controlUrl", - ), + settings = configAdapter.toJson(Config(treatmentUrl = "treatmentUrl", controlUrl = "controlUrl")), assignedCohort = State.Cohort(name = TREATMENT.cohortName, weight = 1, enrollmentDateET = enrollmentDateET), ), ) diff --git a/app/src/test/java/com/duckduckgo/app/trackerdetection/blocklist/BlockListInterceptorApiPluginTest.kt b/app/src/test/java/com/duckduckgo/app/trackerdetection/blocklist/BlockListInterceptorApiPluginTest.kt index b66eb433fb93..6d05736e91ee 100644 --- a/app/src/test/java/com/duckduckgo/app/trackerdetection/blocklist/BlockListInterceptorApiPluginTest.kt +++ b/app/src/test/java/com/duckduckgo/app/trackerdetection/blocklist/BlockListInterceptorApiPluginTest.kt @@ -22,9 +22,6 @@ import com.duckduckgo.app.trackerdetection.api.TDS_BASE_URL import com.duckduckgo.app.trackerdetection.api.TDS_PATH import com.duckduckgo.app.trackerdetection.blocklist.BlockList.Cohorts.CONTROL import com.duckduckgo.app.trackerdetection.blocklist.BlockList.Cohorts.TREATMENT -import com.duckduckgo.app.trackerdetection.blocklist.BlockList.Companion.CONTROL_URL -import com.duckduckgo.app.trackerdetection.blocklist.BlockList.Companion.NEXT_URL -import com.duckduckgo.app.trackerdetection.blocklist.BlockList.Companion.TREATMENT_URL import com.duckduckgo.common.test.CoroutineTestRule import com.duckduckgo.common.test.api.FakeChain import com.duckduckgo.feature.toggles.api.FakeToggleStore @@ -34,6 +31,7 @@ import com.duckduckgo.feature.toggles.api.Toggle import com.duckduckgo.feature.toggles.api.Toggle.DefaultValue import com.duckduckgo.feature.toggles.api.Toggle.State import com.duckduckgo.feature.toggles.impl.RealFeatureTogglesInventory +import com.squareup.moshi.Moshi import org.junit.Assert.* import org.junit.Before import org.junit.Rule @@ -49,6 +47,14 @@ class BlockListInterceptorApiPluginTest { private lateinit var testBlockListFeature: TestBlockListFeature private lateinit var inventory: FeatureTogglesInventory private lateinit var interceptor: BlockListInterceptorApiPlugin + private val moshi = Moshi.Builder().build() + + private data class Config( + val treatmentUrl: String? = null, + val controlUrl: String? = null, + val nextUrl: String? = null, + ) + private val configAdapter = moshi.adapter(Config::class.java) @Before fun setup() { @@ -69,7 +75,7 @@ class BlockListInterceptorApiPluginTest { coroutineRule.testDispatcherProvider, ) - interceptor = BlockListInterceptorApiPlugin(inventory) + interceptor = BlockListInterceptorApiPlugin(inventory, moshi) } @Test @@ -78,9 +84,8 @@ class BlockListInterceptorApiPluginTest { State( remoteEnableState = true, enable = true, - config = mapOf( - TREATMENT_URL to "treatmentUrl", - CONTROL_URL to "controlUrl", + settings = configAdapter.toJson( + Config(treatmentUrl = "treatmentUrl", controlUrl = "controlUrl"), ), cohorts = listOf( State.Cohort(name = CONTROL.cohortName, weight = 0), @@ -92,9 +97,8 @@ class BlockListInterceptorApiPluginTest { State( remoteEnableState = true, enable = true, - config = mapOf( - TREATMENT_URL to "anotherTreatmentUrl", - CONTROL_URL to "anotherControlUrl", + settings = configAdapter.toJson( + Config(treatmentUrl = "anotherTreatmentUrl", controlUrl = "anotherControlUrl"), ), cohorts = listOf( State.Cohort(name = CONTROL.cohortName, weight = 0), @@ -114,9 +118,8 @@ class BlockListInterceptorApiPluginTest { State( remoteEnableState = true, enable = true, - config = mapOf( - TREATMENT_URL to "anotherTreatmentUrl", - CONTROL_URL to "anotherControlUrl", + settings = configAdapter.toJson( + Config(treatmentUrl = "anotherTreatmentUrl", controlUrl = "anotherControlUrl"), ), cohorts = listOf( State.Cohort(name = CONTROL.cohortName, weight = 0), @@ -136,9 +139,8 @@ class BlockListInterceptorApiPluginTest { State( remoteEnableState = true, enable = true, - config = mapOf( - TREATMENT_URL to "anotherTreatmentUrl", - CONTROL_URL to "anotherControlUrl", + settings = configAdapter.toJson( + Config(treatmentUrl = "anotherTreatmentUrl", controlUrl = "anotherControlUrl"), ), cohorts = listOf( State.Cohort(name = CONTROL.cohortName, weight = 1), @@ -158,8 +160,8 @@ class BlockListInterceptorApiPluginTest { State( remoteEnableState = true, enable = true, - config = mapOf( - NEXT_URL to "nextUrl", + settings = configAdapter.toJson( + Config(nextUrl = "nextUrl"), ), ), ) @@ -182,9 +184,8 @@ class BlockListInterceptorApiPluginTest { State( remoteEnableState = true, enable = true, - config = mapOf( - TREATMENT_URL to "anotherTreatmentUrl", - CONTROL_URL to "anotherControlUrl", + settings = configAdapter.toJson( + Config(treatmentUrl = "anotherTreatmentUrl", controlUrl = "anotherControlUrl"), ), cohorts = listOf( State.Cohort(name = CONTROL.cohortName, weight = 1), diff --git a/app/src/test/java/com/duckduckgo/app/trackerdetection/blocklist/BlockListPrivacyTogglePluginTest.kt b/app/src/test/java/com/duckduckgo/app/trackerdetection/blocklist/BlockListPrivacyTogglePluginTest.kt index 15aa52ba4bc6..19ce4d733874 100644 --- a/app/src/test/java/com/duckduckgo/app/trackerdetection/blocklist/BlockListPrivacyTogglePluginTest.kt +++ b/app/src/test/java/com/duckduckgo/app/trackerdetection/blocklist/BlockListPrivacyTogglePluginTest.kt @@ -3,8 +3,6 @@ package com.duckduckgo.app.trackerdetection.blocklist import android.annotation.SuppressLint import com.duckduckgo.app.statistics.pixels.Pixel import com.duckduckgo.app.trackerdetection.blocklist.BlockList.Cohorts.TREATMENT -import com.duckduckgo.app.trackerdetection.blocklist.BlockList.Companion.CONTROL_URL -import com.duckduckgo.app.trackerdetection.blocklist.BlockList.Companion.TREATMENT_URL import com.duckduckgo.common.test.CoroutineTestRule import com.duckduckgo.feature.toggles.api.FakeToggleStore import com.duckduckgo.feature.toggles.api.FeatureToggles @@ -14,6 +12,7 @@ import com.duckduckgo.feature.toggles.impl.RealFeatureTogglesInventory import com.duckduckgo.privacy.dashboard.api.PrivacyToggleOrigin.BREAKAGE_FORM import com.duckduckgo.privacy.dashboard.api.PrivacyToggleOrigin.DASHBOARD import com.duckduckgo.privacy.dashboard.api.PrivacyToggleOrigin.MENU +import com.squareup.moshi.Moshi import java.time.ZoneId import java.time.ZonedDateTime import java.time.temporal.ChronoUnit @@ -31,6 +30,15 @@ class BlockListPrivacyTogglePluginTest { @get:Rule var coroutineRule = CoroutineTestRule() + private val moshi = Moshi.Builder().build() + + private data class Config( + val treatmentUrl: String? = null, + val controlUrl: String? = null, + val nextUrl: String? = null, + ) + private val configAdapter = moshi.adapter(Config::class.java) + private val pixel: Pixel = mock() private lateinit var testBlockListFeature: TestBlockListFeature private lateinit var inventory: FeatureTogglesInventory @@ -88,10 +96,7 @@ class BlockListPrivacyTogglePluginTest { State( remoteEnableState = true, enable = true, - config = mapOf( - TREATMENT_URL to "treatmentUrl", - CONTROL_URL to "controlUrl", - ), + settings = configAdapter.toJson(Config(treatmentUrl = "treatmentUrl", controlUrl = "controlUrl")), assignedCohort = State.Cohort(name = TREATMENT.cohortName, weight = 1, enrollmentDateET = enrollmentDateET), ), ) diff --git a/autofill/autofill-impl/build.gradle b/autofill/autofill-impl/build.gradle index 8ccbc43a69fc..937214be5f02 100644 --- a/autofill/autofill-impl/build.gradle +++ b/autofill/autofill-impl/build.gradle @@ -74,6 +74,7 @@ dependencies { implementation "net.zetetic:android-database-sqlcipher:_" // Testing dependencies + testImplementation project(':common-test') testImplementation "org.mockito.kotlin:mockito-kotlin:_" testImplementation Testing.junit4 testImplementation AndroidX.core diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/importing/gpm/feature/AutofillImportPasswordSettings.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/importing/gpm/feature/AutofillImportPasswordSettings.kt index 9fa7b124efa8..48039ef97554 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/importing/gpm/feature/AutofillImportPasswordSettings.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/importing/gpm/feature/AutofillImportPasswordSettings.kt @@ -20,8 +20,11 @@ import com.duckduckgo.autofill.api.AutofillFeature import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.di.scopes.AppScope import com.squareup.anvil.annotations.ContributesBinding +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Moshi import javax.inject.Inject import kotlinx.coroutines.withContext +import org.json.JSONObject interface AutofillImportPasswordConfigStore { suspend fun getConfig(): AutofillImportPasswordSettings @@ -37,13 +40,22 @@ data class AutofillImportPasswordSettings( class AutofillImportPasswordConfigStoreImpl @Inject constructor( private val autofillFeature: AutofillFeature, private val dispatchers: DispatcherProvider, + private val moshi: Moshi, ) : AutofillImportPasswordConfigStore { + private val jsonAdapter: JsonAdapter by lazy { + moshi.adapter(CanImportFromGooglePasswordManagerConfig::class.java) + } + override suspend fun getConfig(): AutofillImportPasswordSettings { return withContext(dispatchers.io()) { - val config = autofillFeature.canImportFromGooglePasswordManager().getConfig() - val launchUrl = config[LAUNCH_URL_KEY] ?: LAUNCH_URL_DEFAULT - val javascriptConfig = config[JAVASCRIPT_CONFIG_KEY] ?: JAVASCRIPT_CONFIG_DEFAULT + val config = autofillFeature.canImportFromGooglePasswordManager().getSettings()?.let { + runCatching { + jsonAdapter.fromJson(it) + }.getOrNull() + } + val launchUrl = config?.launchUrl ?: LAUNCH_URL_DEFAULT + val javascriptConfig = config?.javascriptConfig?.toString() ?: JAVASCRIPT_CONFIG_DEFAULT AutofillImportPasswordSettings( canImportFromGooglePasswords = autofillFeature.canImportFromGooglePasswordManager().isEnabled(), @@ -54,10 +66,12 @@ class AutofillImportPasswordConfigStoreImpl @Inject constructor( } companion object { - private const val JAVASCRIPT_CONFIG_KEY = "javascriptConfig" - const val JAVASCRIPT_CONFIG_DEFAULT = "\"{}\"" - - private const val LAUNCH_URL_KEY = "launchUrl" - const val LAUNCH_URL_DEFAULT = "https://passwords.google.com/options?ep=1" + internal const val JAVASCRIPT_CONFIG_DEFAULT = "\"{}\"" + internal const val LAUNCH_URL_DEFAULT = "https://passwords.google.com/options?ep=1" } + + private data class CanImportFromGooglePasswordManagerConfig( + val launchUrl: String? = null, + val javascriptConfig: JSONObject? = null, + ) } diff --git a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/importing/gpm/feature/AutofillImportPasswordConfigStoreImplTest.kt b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/importing/gpm/feature/AutofillImportPasswordConfigStoreImplTest.kt index 7a2db4c7bdb3..5264b3e54335 100644 --- a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/importing/gpm/feature/AutofillImportPasswordConfigStoreImplTest.kt +++ b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/importing/gpm/feature/AutofillImportPasswordConfigStoreImplTest.kt @@ -1,26 +1,36 @@ package com.duckduckgo.autofill.impl.importing.gpm.feature import android.annotation.SuppressLint +import androidx.test.ext.junit.runners.AndroidJUnit4 import com.duckduckgo.autofill.api.AutofillFeature import com.duckduckgo.autofill.impl.importing.gpm.feature.AutofillImportPasswordConfigStoreImpl.Companion.JAVASCRIPT_CONFIG_DEFAULT import com.duckduckgo.autofill.impl.importing.gpm.feature.AutofillImportPasswordConfigStoreImpl.Companion.LAUNCH_URL_DEFAULT import com.duckduckgo.common.test.CoroutineTestRule +import com.duckduckgo.common.test.json.JSONObjectAdapter import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory import com.duckduckgo.feature.toggles.api.Toggle.State +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Moshi import kotlinx.coroutines.test.runTest import org.junit.Assert.* import org.junit.Rule import org.junit.Test +import org.junit.runner.RunWith +@RunWith(AndroidJUnit4::class) class AutofillImportPasswordConfigStoreImplTest { @get:Rule val coroutineTestRule: CoroutineTestRule = CoroutineTestRule() + private val moshi = Moshi.Builder().add(JSONObjectAdapter()).build() + private val adapter: JsonAdapter = moshi.adapter(Config::class.java) + private val autofillFeature = FakeFeatureToggleFactory.create(AutofillFeature::class.java) private val testee = AutofillImportPasswordConfigStoreImpl( autofillFeature = autofillFeature, dispatchers = coroutineTestRule.testDispatcherProvider, + moshi = moshi, ) @Test @@ -37,35 +47,44 @@ class AutofillImportPasswordConfigStoreImplTest { @Test fun whenLaunchUrlNotSpecifiedInConfigThenDefaultUsed() = runTest { - configureFeature(config = emptyMap()) + configureFeature(config = Config()) assertEquals(LAUNCH_URL_DEFAULT, testee.getConfig().launchUrlGooglePasswords) } @Test fun whenLaunchUrlSpecifiedInConfigThenOverridesDefault() = runTest { - configureFeature(config = mapOf("launchUrl" to "https://example.com")) + configureFeature(config = Config(launchUrl = "https://example.com")) assertEquals("https://example.com", testee.getConfig().launchUrlGooglePasswords) } @Test fun whenJavascriptConfigNotSpecifiedInConfigThenDefaultUsed() = runTest { - configureFeature(config = emptyMap()) + configureFeature(config = Config()) assertEquals(JAVASCRIPT_CONFIG_DEFAULT, testee.getConfig().javascriptConfigGooglePasswords) } @Test fun whenJavascriptConfigSpecifiedInConfigThenOverridesDefault() = runTest { - configureFeature(config = mapOf("javascriptConfig" to """{"key": "value"}""")) - assertEquals("""{"key": "value"}""", testee.getConfig().javascriptConfigGooglePasswords) + configureFeature(config = Config(javascriptConfig = JavaScriptConfig(key = "value", domains = listOf("foo, bar")))) + assertEquals("""{"domains":["foo, bar"],"key":"value"}""", testee.getConfig().javascriptConfigGooglePasswords) } @SuppressLint("DenyListedApi") - private fun configureFeature(enabled: Boolean = true, config: Map = emptyMap()) { + private fun configureFeature(enabled: Boolean = true, config: Config = Config()) { autofillFeature.canImportFromGooglePasswordManager().setRawStoredState( State( remoteEnableState = enabled, - config = config, + settings = adapter.toJson(config), ), ) } + + private data class Config( + val launchUrl: String? = null, + val javascriptConfig: JavaScriptConfig? = null, + ) + private data class JavaScriptConfig( + val key: String, + val domains: List, + ) } diff --git a/common/common-test/build.gradle b/common/common-test/build.gradle index 1d813a7d3da1..755ba72bccb8 100644 --- a/common/common-test/build.gradle +++ b/common/common-test/build.gradle @@ -16,6 +16,7 @@ dependencies { implementation "io.reactivex.rxjava2:rxjava:_" implementation "io.reactivex.rxjava2:rxandroid:_" + implementation "com.squareup.moshi:moshi-kotlin:_" implementation Square.okHttp3.okHttp implementation KotlinX.coroutines.core // api because TestDispatcher is in the CoroutineTestRule public API diff --git a/common/common-test/src/main/java/com/duckduckgo/common/test/json/JSONObjectAdapter.kt b/common/common-test/src/main/java/com/duckduckgo/common/test/json/JSONObjectAdapter.kt new file mode 100644 index 000000000000..59f7271944f9 --- /dev/null +++ b/common/common-test/src/main/java/com/duckduckgo/common/test/json/JSONObjectAdapter.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 DuckDuckGo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.duckduckgo.common.test.json + +import com.squareup.moshi.FromJson +import com.squareup.moshi.JsonReader +import com.squareup.moshi.JsonWriter +import com.squareup.moshi.ToJson +import okio.Buffer +import org.json.JSONException +import org.json.JSONObject + +class JSONObjectAdapter { + + @FromJson + fun fromJson(reader: JsonReader): JSONObject? { + // Here we're expecting the JSON object, it is processed as Map by Moshi + return (reader.readJsonValue() as? Map<*, *>)?.let { data -> + try { + JSONObject(data) + } catch (e: JSONException) { + // Handle exception + return null + } + } + } + + @ToJson + fun toJson( + writer: JsonWriter, + value: JSONObject?, + ) { + value?.let { writer.run { value(Buffer().writeUtf8(value.toString())) } } + } +} diff --git a/feature-toggles/feature-toggles-api/src/main/java/com/duckduckgo/feature/toggles/api/FeatureSettings.kt b/feature-toggles/feature-toggles-api/src/main/java/com/duckduckgo/feature/toggles/api/FeatureSettings.kt index 9635a5c5d59e..377c37b83cf2 100644 --- a/feature-toggles/feature-toggles-api/src/main/java/com/duckduckgo/feature/toggles/api/FeatureSettings.kt +++ b/feature-toggles/feature-toggles-api/src/main/java/com/duckduckgo/feature/toggles/api/FeatureSettings.kt @@ -15,7 +15,9 @@ */ package com.duckduckgo.feature.toggles.api +@Deprecated(message = "Not needed anymore. Settings is now supported in top-leve and sub-features and Toggle#getSettings returns it") object FeatureSettings { + @Deprecated(message = "Not needed anymore. Settings is now supported in top-leve and sub-features and Toggle#getSettings returns it") interface Store { fun store( jsonString: String, diff --git a/feature-toggles/feature-toggles-api/src/main/java/com/duckduckgo/feature/toggles/api/FeatureToggles.kt b/feature-toggles/feature-toggles-api/src/main/java/com/duckduckgo/feature/toggles/api/FeatureToggles.kt index 154419ad14c3..5b46860d3563 100644 --- a/feature-toggles/feature-toggles-api/src/main/java/com/duckduckgo/feature/toggles/api/FeatureToggles.kt +++ b/feature-toggles/feature-toggles-api/src/main/java/com/duckduckgo/feature/toggles/api/FeatureToggles.kt @@ -194,9 +194,9 @@ interface Toggle { fun getRawStoredState(): State? /** - * @return a Map of containing the config of the feature or an empty map + * @return a JSON string containing the `settings`` of the feature or null if not present in the remote config */ - fun getConfig(): Map + fun getSettings(): String? /** * @return a [Cohort] if one has been assigned or `null` otherwise. @@ -224,7 +224,7 @@ interface Toggle { val metadataInfo: String? = null, val cohorts: List = emptyList(), val assignedCohort: Cohort? = null, - val config: Map = emptyMap(), + val settings: String? = null, ) { data class Target( val variantKey: String?, @@ -521,7 +521,7 @@ internal class ToggleImpl constructor( ) } - override fun getConfig(): Map = store.get(key)?.config ?: emptyMap() + override fun getSettings(): String? = store.get(key)?.settings override fun getCohort(): Cohort? { return store.get(key)?.assignedCohort diff --git a/feature-toggles/feature-toggles-impl/lint-baseline.xml b/feature-toggles/feature-toggles-impl/lint-baseline.xml index d6dcefa52c78..1a704e9c033e 100644 --- a/feature-toggles/feature-toggles-impl/lint-baseline.xml +++ b/feature-toggles/feature-toggles-impl/lint-baseline.xml @@ -8,7 +8,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -19,7 +19,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -30,7 +30,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -41,7 +41,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -52,7 +52,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -63,7 +63,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -74,7 +74,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -85,7 +85,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -96,7 +96,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -107,7 +107,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -118,7 +118,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -129,7 +129,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -140,7 +140,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -151,7 +151,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -162,7 +162,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -173,7 +173,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -184,7 +184,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -195,7 +195,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -206,7 +206,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -217,7 +217,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -228,7 +228,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -239,7 +239,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -250,7 +250,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -261,7 +261,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -272,7 +272,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -283,7 +283,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -294,7 +294,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -305,7 +305,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -316,7 +316,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -327,7 +327,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -338,7 +338,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -349,7 +349,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -360,7 +360,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -371,7 +371,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -382,7 +382,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -393,7 +393,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -404,7 +404,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -415,7 +415,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -426,7 +426,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -437,7 +437,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -448,7 +448,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -459,7 +459,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -470,7 +470,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -481,51 +481,62 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + + + + @@ -536,7 +547,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~"> @@ -855,7 +866,7 @@ errorLine2=" ^"> @@ -866,7 +877,7 @@ errorLine2=" ^"> @@ -877,7 +888,7 @@ errorLine2=" ^"> @@ -888,7 +899,7 @@ errorLine2=" ^"> @@ -899,7 +910,7 @@ errorLine2=" ^"> @@ -910,7 +921,7 @@ errorLine2=" ^"> @@ -921,7 +932,7 @@ errorLine2=" ^"> @@ -932,7 +943,7 @@ errorLine2=" ^"> @@ -943,7 +954,7 @@ errorLine2=" ^"> @@ -965,7 +976,7 @@ errorLine2=" ^"> @@ -976,29 +987,7 @@ errorLine2=" ^"> - - - - - - - - @@ -1009,7 +998,7 @@ errorLine2=" ^"> @@ -1020,7 +1009,7 @@ errorLine2=" ^"> @@ -1031,7 +1020,7 @@ errorLine2=" ^"> @@ -1042,7 +1031,7 @@ errorLine2=" ^"> @@ -1053,7 +1042,7 @@ errorLine2=" ^"> @@ -1064,7 +1053,7 @@ errorLine2=" ^"> @@ -1075,7 +1064,7 @@ errorLine2=" ^"> @@ -1086,7 +1075,7 @@ errorLine2=" ^"> diff --git a/feature-toggles/feature-toggles-impl/src/test/java/com/duckduckgo/feature/toggles/codegen/ContributesRemoteFeatureCodeGeneratorTest.kt b/feature-toggles/feature-toggles-impl/src/test/java/com/duckduckgo/feature/toggles/codegen/ContributesRemoteFeatureCodeGeneratorTest.kt index 6e30501edcd6..ddf18a59e6b4 100644 --- a/feature-toggles/feature-toggles-impl/src/test/java/com/duckduckgo/feature/toggles/codegen/ContributesRemoteFeatureCodeGeneratorTest.kt +++ b/feature-toggles/feature-toggles-impl/src/test/java/com/duckduckgo/feature/toggles/codegen/ContributesRemoteFeatureCodeGeneratorTest.kt @@ -36,6 +36,8 @@ import com.duckduckgo.feature.toggles.codegen.ContributesRemoteFeatureCodeGenera import com.duckduckgo.privacy.config.api.PrivacyFeaturePlugin import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesMultibinding +import com.squareup.moshi.Moshi +import com.squareup.moshi.Types import dagger.Lazy import dagger.SingleInstanceIn import java.time.ZoneId @@ -3525,6 +3527,10 @@ class ContributesRemoteFeatureCodeGeneratorTest { @Test fun `test config parsed correctly`() { + val moshi = Moshi.Builder().build() + val adapter = moshi.adapter>( + Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java), + ) val feature = generatedFeatureNewInstance() val privacyPlugin = (feature as PrivacyFeaturePlugin) @@ -3536,12 +3542,30 @@ class ContributesRemoteFeatureCodeGeneratorTest { { "hash": "1", "state": "disabled", + "settings": { + "foo": "foo/value", + "bar": { + "key": "value", + "number": 2, + "boolean": true, + "complex": { + "boolean": true + } + } + }, "features": { "fooFeature": { "state": "enabled", - "config": { + "settings": { "foo": "foo/value", - "bar": "bar/value" + "bar": { + "key": "value", + "number": 2, + "boolean": true, + "complex": { + "boolean": true + } + } }, "rollout": { "steps": [ @@ -3567,14 +3591,23 @@ class ContributesRemoteFeatureCodeGeneratorTest { ), ) - var stateConfig = testFeature.fooFeature().getRawStoredState()?.config!! - var config = testFeature.fooFeature().getConfig() + val topLevelStateConfig = testFeature.self().getRawStoredState()?.settings?.let { adapter.fromJson(it) } ?: emptyMap() + val topLevelConfig = testFeature.self().getSettings()?.let { adapter.fromJson(it) } ?: emptyMap() + assertTrue(topLevelStateConfig.size == 2) + assertEquals("foo/value", topLevelStateConfig["foo"]) + assertEquals(mapOf("key" to "value", "number" to 2.0, "boolean" to true, "complex" to mapOf("boolean" to true)), topLevelStateConfig["bar"]) + assertTrue(topLevelConfig.size == 2) + assertEquals("foo/value", topLevelConfig["foo"]) + assertEquals(mapOf("key" to "value", "number" to 2.0, "boolean" to true, "complex" to mapOf("boolean" to true)), topLevelConfig["bar"]) + + var stateConfig = testFeature.fooFeature().getRawStoredState()?.settings?.let { adapter.fromJson(it) } ?: emptyMap() + var config = testFeature.fooFeature().getSettings()?.let { adapter.fromJson(it) } ?: emptyMap() assertTrue(stateConfig.size == 2) assertEquals("foo/value", stateConfig["foo"]) - assertEquals("bar/value", stateConfig["bar"]) + assertEquals(mapOf("key" to "value", "number" to 2.0, "boolean" to true, "complex" to mapOf("boolean" to true)), stateConfig["bar"]) assertTrue(config.size == 2) assertEquals("foo/value", config["foo"]) - assertEquals("bar/value", config["bar"]) + assertEquals(mapOf("key" to "value", "number" to 2.0, "boolean" to true, "complex" to mapOf("boolean" to true)), config["bar"]) // Delete config key, should remove assertTrue( @@ -3587,7 +3620,7 @@ class ContributesRemoteFeatureCodeGeneratorTest { "features": { "fooFeature": { "state": "enabled", - "config": { + "settings": { "foo": "foo/value" }, "rollout": { @@ -3614,8 +3647,8 @@ class ContributesRemoteFeatureCodeGeneratorTest { ), ) - stateConfig = testFeature.fooFeature().getRawStoredState()?.config!! - config = testFeature.fooFeature().getConfig() + stateConfig = testFeature.fooFeature().getRawStoredState()?.settings?.let { adapter.fromJson(it) } ?: emptyMap() + config = testFeature.fooFeature().getSettings()?.let { adapter.fromJson(it) } ?: emptyMap() assertTrue(stateConfig.size == 1) assertEquals("foo/value", stateConfig["foo"]) assertNull(stateConfig["bar"]) @@ -3623,7 +3656,7 @@ class ContributesRemoteFeatureCodeGeneratorTest { assertEquals("foo/value", config["foo"]) assertNull(config["bar"]) - // delete config, returns empty map + // delete config, returns empty assertTrue( privacyPlugin.store( "testFeature", @@ -3654,8 +3687,8 @@ class ContributesRemoteFeatureCodeGeneratorTest { ), ) - stateConfig = testFeature.fooFeature().getRawStoredState()?.config!! - config = testFeature.fooFeature().getConfig() + stateConfig = testFeature.fooFeature().getRawStoredState()?.settings?.let { adapter.fromJson(it) } ?: emptyMap() + config = testFeature.fooFeature().getSettings()?.let { adapter.fromJson(it) } ?: emptyMap() assertTrue(stateConfig.isEmpty()) assertTrue(config.isEmpty()) @@ -3670,7 +3703,7 @@ class ContributesRemoteFeatureCodeGeneratorTest { "features": { "fooFeature": { "state": "enabled", - "config": { + "settings": { "x": "x/value", "y": "y/value" }, @@ -3698,8 +3731,8 @@ class ContributesRemoteFeatureCodeGeneratorTest { ), ) - stateConfig = testFeature.fooFeature().getRawStoredState()?.config!! - config = testFeature.fooFeature().getConfig() + stateConfig = testFeature.fooFeature().getRawStoredState()?.settings?.let { adapter.fromJson(it) } ?: emptyMap() + config = testFeature.fooFeature().getSettings()?.let { adapter.fromJson(it) } ?: emptyMap() assertTrue(stateConfig.size == 2) assertEquals("x/value", stateConfig["x"]) assertEquals("y/value", stateConfig["y"])