diff --git a/app/src/main/java/com/tangem/tap/TangemApplication.kt b/app/src/main/java/com/tangem/tap/TangemApplication.kt index 47f5d7142c..7468d27129 100644 --- a/app/src/main/java/com/tangem/tap/TangemApplication.kt +++ b/app/src/main/java/com/tangem/tap/TangemApplication.kt @@ -16,7 +16,6 @@ import com.tangem.datasource.api.common.MoshiConverter import com.tangem.datasource.api.common.createNetworkLoggingInterceptor import com.tangem.datasource.asset.loader.AssetLoader import com.tangem.datasource.config.ConfigManager -import com.tangem.datasource.config.FeaturesLocalLoader import com.tangem.datasource.config.models.Config import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.datasource.local.preferences.AppPreferencesStore @@ -202,10 +201,11 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { // [REDACTED_JIRA] runBlocking { featureTogglesManager.init() - initConfigManager( - loader = FeaturesLocalLoader(assetLoader, BuildConfig.ENVIRONMENT), - onComplete = ::initWithConfigDependency, - ) + + val config = configManager.initialize() + store.dispatch(GlobalAction.SetConfigManager(configManager)) + + initWithConfigDependency(config = config) } loadNativeLibraries() @@ -223,7 +223,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { ) appStateHolder.mainStore = store - walletConnect2Repository.init(projectId = configManager.config.walletConnectProjectId) + walletConnect2Repository.init(projectId = configManager.getConfigSync().walletConnectProjectId) } private fun createReduxStore(): Store { @@ -278,13 +278,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { System.loadLibrary("TrustWalletCore") } - private suspend fun initConfigManager(loader: FeaturesLocalLoader, onComplete: (Config) -> Unit) { - configManager.load(loader) { config -> - store.dispatch(GlobalAction.SetConfigManager(configManager)) - onComplete(config) - } - } - private fun initWithConfigDependency(config: Config) { initAnalytics(this, config) Log.addLogger(logger = tangemSdkLogger) diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt index 4fd7a149b6..2d3356afae 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt @@ -57,7 +57,7 @@ private fun handleAction(action: Action, appState: () -> AppState?) { } is GlobalAction.ExchangeManager.Init -> { val appStateSafe = appState() ?: return - val config = appStateSafe.globalState.configManager?.config ?: return + val config = appStateSafe.globalState.configManager?.getConfigSync() ?: return scope.launch { val scanResponseProvider: () -> ScanResponse? = { diff --git a/app/src/main/java/com/tangem/tap/network/auth/DefaultStakeKitAuthProvider.kt b/app/src/main/java/com/tangem/tap/network/auth/DefaultStakeKitAuthProvider.kt index a5ef219c9b..2e8c9cde6d 100644 --- a/app/src/main/java/com/tangem/tap/network/auth/DefaultStakeKitAuthProvider.kt +++ b/app/src/main/java/com/tangem/tap/network/auth/DefaultStakeKitAuthProvider.kt @@ -8,6 +8,6 @@ internal class DefaultStakeKitAuthProvider( ) : StakeKitAuthProvider { override fun getApiKey(): String { - return configManager.config.stakeKitApiKey ?: error("No StakeKit api key provided") + return configManager.getConfigSync().stakeKitApiKey ?: error("No StakeKit api key provided") } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/config/Express.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/config/Express.kt index 134d24a3ac..e87b6490b9 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/common/config/Express.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/config/Express.kt @@ -55,9 +55,9 @@ internal class Express( private fun getApiKey(isProd: Boolean): String { return if (isProd) { - configManager.config.express + configManager.getConfigSync().express } else { - configManager.config.devExpress + configManager.getConfigSync().devExpress } ?.apiKey ?: error("No express config provided") diff --git a/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManager.kt b/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManager.kt index ac8c927826..870bd611a2 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManager.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManager.kt @@ -1,14 +1,21 @@ package com.tangem.datasource.config import com.tangem.datasource.config.models.Config -import com.tangem.datasource.config.models.ConfigModel +import kotlinx.coroutines.flow.Flow /** + * Config manager + * [REDACTED_AUTHOR] */ interface ConfigManager { - val config: Config + /** Initialize and return [Config] */ + suspend fun initialize(): Config - suspend fun load(configLoader: Loader, onComplete: ((config: Config) -> Unit)? = null) + /** Get [Config] as [Flow] */ + fun getConfig(): Flow + + /** Get [Config] synchronously */ + fun getConfigSync(): Config } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt b/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt deleted file mode 100644 index 62b5065feb..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt +++ /dev/null @@ -1,119 +0,0 @@ -package com.tangem.datasource.config - -import com.tangem.blockchain.common.* -import com.tangem.datasource.config.models.Config -import com.tangem.datasource.config.models.ConfigModel -import com.tangem.datasource.config.models.ConfigValueModel -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -internal class ConfigManagerImpl @Inject constructor() : ConfigManager { - - override var config: Config = Config() - private set - - override suspend fun load(configLoader: Loader, onComplete: ((config: Config) -> Unit)?) { - configLoader.load { configModel -> - setupConfigValues(configModel.configValues) - onComplete?.invoke(config) - } - } - - private fun setupConfigValues(configValues: ConfigValueModel?) { - val values = configValues ?: return - - config = createConfig(config, values) - } - - private fun createConfig(config: Config, configValues: ConfigValueModel): Config { - return config.copy( - moonPayApiKey = configValues.moonPayApiKey, - moonPayApiSecretKey = configValues.moonPayApiSecretKey, - mercuryoWidgetId = configValues.mercuryoWidgetId, - mercuryoSecret = configValues.mercuryoSecret, - blockchainSdkConfig = BlockchainSdkConfig( - blockchairCredentials = BlockchairCredentials( - apiKey = configValues.blockchairApiKeys, - authToken = configValues.blockchairAuthorizationToken, - ), - blockcypherTokens = configValues.blockcypherTokens, - quickNodeSolanaCredentials = QuickNodeCredentials( - apiKey = configValues.quiknodeApiKey, - subdomain = configValues.quiknodeSubdomain, - ), - quickNodeBscCredentials = QuickNodeCredentials( - apiKey = configValues.bscQuiknodeApiKey, - subdomain = configValues.bscQuiknodeSubdomain, - ), - infuraProjectId = configValues.infuraProjectId, - tronGridApiKey = configValues.tronGridApiKey, - nowNodeCredentials = NowNodeCredentials(configValues.nowNodesApiKey), - getBlockCredentials = createGetBlockCredentials(configValues), - kaspaSecondaryApiUrl = configValues.kaspaSecondaryApiUrl, - tonCenterCredentials = TonCenterCredentials( - mainnetApiKey = configValues.tonCenterKeys.mainnet, - testnetApiKey = configValues.tonCenterKeys.testnet, - ), - chiaFireAcademyApiKey = configValues.chiaFireAcademyApiKey, - chiaTangemApiKey = configValues.chiaTangemApiKey, - hederaArkhiaApiKey = configValues.hederaArkhiaKey, - polygonScanApiKey = configValues.polygonScanApiKey, - bittensorDwellirApiKey = configValues.bittensorDwellirApiKey, - bittensorOnfinalityApiKey = configValues.bittensorOnfinalityKey, - koinosProApiKey = configValues.koinosProApiKey, - ), - amplitudeApiKey = configValues.amplitudeApiKey, - walletConnectProjectId = configValues.walletConnectProjectId, - express = configValues.express, - devExpress = configValues.devExpress, - stakeKitApiKey = configValues.stakeKitApiKey, - ) - } - - private fun createGetBlockCredentials(configValues: ConfigValueModel): GetBlockCredentials? { - return configValues.getBlockAccessTokens?.let { accessTokens -> - GetBlockCredentials( - xrp = GetBlockAccessToken(jsonRpc = accessTokens.xrp?.jsonRPC), - cardano = GetBlockAccessToken(rosetta = accessTokens.cardano?.rosetta), - avalanche = GetBlockAccessToken(jsonRpc = accessTokens.avalanche?.jsonRPC), - eth = GetBlockAccessToken(jsonRpc = accessTokens.eth?.jsonRPC), - etc = GetBlockAccessToken(jsonRpc = accessTokens.etc?.jsonRPC), - fantom = GetBlockAccessToken(jsonRpc = accessTokens.fantom?.jsonRPC), - rsk = GetBlockAccessToken(jsonRpc = accessTokens.rsk?.jsonRPC), - bsc = GetBlockAccessToken(jsonRpc = accessTokens.bsc?.jsonRPC), - polygon = GetBlockAccessToken(jsonRpc = accessTokens.polygon?.jsonRPC), - gnosis = GetBlockAccessToken(jsonRpc = accessTokens.gnosis?.jsonRPC), - cronos = GetBlockAccessToken(jsonRpc = accessTokens.cronos?.jsonRPC), - solana = GetBlockAccessToken(jsonRpc = accessTokens.solana?.jsonRPC), - ton = GetBlockAccessToken(jsonRpc = accessTokens.ton?.jsonRPC), - tron = GetBlockAccessToken(rest = accessTokens.tron?.rest), - cosmos = GetBlockAccessToken(rest = accessTokens.cosmos?.rest), - near = GetBlockAccessToken(jsonRpc = accessTokens.near?.jsonRPC), - aptos = GetBlockAccessToken(rest = accessTokens.aptos?.rest), - dogecoin = GetBlockAccessToken( - jsonRpc = accessTokens.dogecoin?.jsonRPC, - blockBookRest = accessTokens.dogecoin?.blockBookRest, - ), - litecoin = GetBlockAccessToken( - jsonRpc = accessTokens.litecoin?.jsonRPC, - blockBookRest = accessTokens.litecoin?.blockBookRest, - ), - dash = GetBlockAccessToken( - jsonRpc = accessTokens.dash?.jsonRPC, - blockBookRest = accessTokens.dash?.blockBookRest, - ), - bitcoin = GetBlockAccessToken( - jsonRpc = accessTokens.bitcoin?.jsonRPC, - blockBookRest = accessTokens.bitcoin?.blockBookRest, - ), - algorand = GetBlockAccessToken(rest = accessTokens.algorand?.rest), - zkSyncEra = GetBlockAccessToken(rest = accessTokens.zksync?.jsonRPC), - polygonZkEvm = GetBlockAccessToken(rest = accessTokens.polygonZkevm?.jsonRPC), - base = GetBlockAccessToken(rest = accessTokens.base?.jsonRPC), - blast = GetBlockAccessToken(jsonRpc = accessTokens.blast?.jsonRPC), - filecoin = GetBlockAccessToken(jsonRpc = accessTokens.filecoin?.jsonRPC), - ) - } - } -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/config/DefaultConfigManager.kt b/core/datasource/src/main/java/com/tangem/datasource/config/DefaultConfigManager.kt new file mode 100644 index 0000000000..68269dcbbc --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/config/DefaultConfigManager.kt @@ -0,0 +1,42 @@ +package com.tangem.datasource.config + +import com.tangem.datasource.BuildConfig +import com.tangem.datasource.asset.loader.AssetLoader +import com.tangem.datasource.config.converter.EnvironmentConfigConverter +import com.tangem.datasource.config.models.Config +import com.tangem.datasource.config.models.ConfigValueModel +import com.tangem.datasource.local.datastore.RuntimeStateStore +import kotlinx.coroutines.flow.Flow +import timber.log.Timber + +/** + * Default config manager + * + * @property assetLoader asset loader + * @property configStore config store + */ +internal class DefaultConfigManager( + private val assetLoader: AssetLoader, + private val configStore: RuntimeStateStore, +) : ConfigManager { + + override suspend fun initialize(): Config { + val configValueModel = assetLoader.load(fileName = CONFIG_FILE_NAME) + ?: return configStore.get().value + + val config = EnvironmentConfigConverter.convert(value = configValueModel) + configStore.store(value = config) + + Timber.i("Config [$CONFIG_FILE_NAME] loaded successfully") + + return config + } + + override fun getConfig(): Flow = configStore.get() + + override fun getConfigSync(): Config = configStore.get().value + + private companion object { + const val CONFIG_FILE_NAME = "tangem-app-config/config_${BuildConfig.ENVIRONMENT}" + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/config/FeaturesLocalLoader.kt b/core/datasource/src/main/java/com/tangem/datasource/config/FeaturesLocalLoader.kt deleted file mode 100644 index c9e890f0bb..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/config/FeaturesLocalLoader.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.tangem.datasource.config - -import com.tangem.datasource.asset.loader.AssetLoader -import com.tangem.datasource.config.models.ConfigModel -import com.tangem.datasource.config.models.ConfigValueModel - -/** -[REDACTED_AUTHOR] - */ -class FeaturesLocalLoader( - private val assetLoader: AssetLoader, - buildEnvironment: String, -) : Loader { - - private val configValuesName = "tangem-app-config/config_$buildEnvironment" - - override suspend fun load(onComplete: (ConfigModel) -> Unit) { - ConfigModel( - configValues = assetLoader.load(fileName = configValuesName), - ).also(onComplete) - } -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/config/Loader.kt b/core/datasource/src/main/java/com/tangem/datasource/config/Loader.kt deleted file mode 100644 index c5f9b0073e..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/config/Loader.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.tangem.datasource.config - -/** -[REDACTED_AUTHOR] - */ -interface Loader { - suspend fun load(onComplete: (T) -> Unit) -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/config/converter/BlockchainSDKConfigConverter.kt b/core/datasource/src/main/java/com/tangem/datasource/config/converter/BlockchainSDKConfigConverter.kt new file mode 100644 index 0000000000..cbe0ec58bd --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/config/converter/BlockchainSDKConfigConverter.kt @@ -0,0 +1,93 @@ +package com.tangem.datasource.config.converter + +import com.tangem.blockchain.common.* +import com.tangem.datasource.config.models.ConfigValueModel +import com.tangem.utils.converter.Converter + +/** + * Converts [ConfigValueModel] to [BlockchainSdkConfig] + * +[REDACTED_AUTHOR] + */ +internal object BlockchainSDKConfigConverter : Converter { + + override fun convert(value: ConfigValueModel): BlockchainSdkConfig { + return BlockchainSdkConfig( + blockchairCredentials = BlockchairCredentials( + apiKey = value.blockchairApiKeys, + authToken = value.blockchairAuthorizationToken, + ), + blockcypherTokens = value.blockcypherTokens, + quickNodeSolanaCredentials = QuickNodeCredentials( + apiKey = value.quiknodeApiKey, + subdomain = value.quiknodeSubdomain, + ), + quickNodeBscCredentials = QuickNodeCredentials( + apiKey = value.bscQuiknodeApiKey, + subdomain = value.bscQuiknodeSubdomain, + ), + infuraProjectId = value.infuraProjectId, + tronGridApiKey = value.tronGridApiKey, + nowNodeCredentials = NowNodeCredentials(value.nowNodesApiKey), + getBlockCredentials = createGetBlockCredentials(value), + kaspaSecondaryApiUrl = value.kaspaSecondaryApiUrl, + tonCenterCredentials = TonCenterCredentials( + mainnetApiKey = value.tonCenterKeys.mainnet, + testnetApiKey = value.tonCenterKeys.testnet, + ), + chiaFireAcademyApiKey = value.chiaFireAcademyApiKey, + chiaTangemApiKey = value.chiaTangemApiKey, + hederaArkhiaApiKey = value.hederaArkhiaKey, + polygonScanApiKey = value.polygonScanApiKey, + bittensorDwellirApiKey = value.bittensorDwellirApiKey, + bittensorOnfinalityApiKey = value.bittensorOnfinalityKey, + koinosProApiKey = value.koinosProApiKey, + ) + } + + private fun createGetBlockCredentials(configValues: ConfigValueModel): GetBlockCredentials? { + return configValues.getBlockAccessTokens?.let { accessTokens -> + GetBlockCredentials( + xrp = GetBlockAccessToken(jsonRpc = accessTokens.xrp?.jsonRPC), + cardano = GetBlockAccessToken(rosetta = accessTokens.cardano?.rosetta), + avalanche = GetBlockAccessToken(jsonRpc = accessTokens.avalanche?.jsonRPC), + eth = GetBlockAccessToken(jsonRpc = accessTokens.eth?.jsonRPC), + etc = GetBlockAccessToken(jsonRpc = accessTokens.etc?.jsonRPC), + fantom = GetBlockAccessToken(jsonRpc = accessTokens.fantom?.jsonRPC), + rsk = GetBlockAccessToken(jsonRpc = accessTokens.rsk?.jsonRPC), + bsc = GetBlockAccessToken(jsonRpc = accessTokens.bsc?.jsonRPC), + polygon = GetBlockAccessToken(jsonRpc = accessTokens.polygon?.jsonRPC), + gnosis = GetBlockAccessToken(jsonRpc = accessTokens.gnosis?.jsonRPC), + cronos = GetBlockAccessToken(jsonRpc = accessTokens.cronos?.jsonRPC), + solana = GetBlockAccessToken(jsonRpc = accessTokens.solana?.jsonRPC), + ton = GetBlockAccessToken(jsonRpc = accessTokens.ton?.jsonRPC), + tron = GetBlockAccessToken(rest = accessTokens.tron?.rest), + cosmos = GetBlockAccessToken(rest = accessTokens.cosmos?.rest), + near = GetBlockAccessToken(jsonRpc = accessTokens.near?.jsonRPC), + aptos = GetBlockAccessToken(rest = accessTokens.aptos?.rest), + dogecoin = GetBlockAccessToken( + jsonRpc = accessTokens.dogecoin?.jsonRPC, + blockBookRest = accessTokens.dogecoin?.blockBookRest, + ), + litecoin = GetBlockAccessToken( + jsonRpc = accessTokens.litecoin?.jsonRPC, + blockBookRest = accessTokens.litecoin?.blockBookRest, + ), + dash = GetBlockAccessToken( + jsonRpc = accessTokens.dash?.jsonRPC, + blockBookRest = accessTokens.dash?.blockBookRest, + ), + bitcoin = GetBlockAccessToken( + jsonRpc = accessTokens.bitcoin?.jsonRPC, + blockBookRest = accessTokens.bitcoin?.blockBookRest, + ), + algorand = GetBlockAccessToken(rest = accessTokens.algorand?.rest), + zkSyncEra = GetBlockAccessToken(jsonRpc = accessTokens.zksync?.jsonRPC), + polygonZkEvm = GetBlockAccessToken(jsonRpc = accessTokens.polygonZkevm?.jsonRPC), + base = GetBlockAccessToken(jsonRpc = accessTokens.base?.jsonRPC), + blast = GetBlockAccessToken(jsonRpc = accessTokens.blast?.jsonRPC), + filecoin = GetBlockAccessToken(jsonRpc = accessTokens.filecoin?.jsonRPC), + ) + } + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/config/converter/EnvironmentConfigConverter.kt b/core/datasource/src/main/java/com/tangem/datasource/config/converter/EnvironmentConfigConverter.kt new file mode 100644 index 0000000000..bd78bce60e --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/config/converter/EnvironmentConfigConverter.kt @@ -0,0 +1,28 @@ +package com.tangem.datasource.config.converter + +import com.tangem.datasource.config.models.Config +import com.tangem.datasource.config.models.ConfigValueModel +import com.tangem.utils.converter.Converter + +/** + * Converter from [ConfigValueModel] to [Config] + * +[REDACTED_AUTHOR] + */ +internal object EnvironmentConfigConverter : Converter { + + override fun convert(value: ConfigValueModel): Config { + return Config( + moonPayApiKey = value.moonPayApiKey, + moonPayApiSecretKey = value.moonPayApiSecretKey, + mercuryoWidgetId = value.mercuryoWidgetId, + mercuryoSecret = value.mercuryoSecret, + blockchainSdkConfig = BlockchainSDKConfigConverter.convert(value = value), + amplitudeApiKey = value.amplitudeApiKey, + walletConnectProjectId = value.walletConnectProjectId, + express = value.express, + devExpress = value.devExpress, + stakeKitApiKey = value.stakeKitApiKey, + ) + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt b/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt index 56c6b645c6..3a88d95799 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt @@ -74,12 +74,4 @@ data class GetBlockToken( @Json(name = "blockBookRest") val blockBookRest: String?, @Json(name = "rest") val rest: String?, @Json(name = "rosetta") val rosetta: String?, -) - -class ConfigModel( - val configValues: ConfigValueModel?, -) { - companion object { - fun empty(): ConfigModel = ConfigModel(null) - } -} \ No newline at end of file +) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/crypto/Sha256SignatureVerifier.kt b/core/datasource/src/main/java/com/tangem/datasource/crypto/Sha256SignatureVerifier.kt index 5353f15353..1452e5f68b 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/crypto/Sha256SignatureVerifier.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/crypto/Sha256SignatureVerifier.kt @@ -24,8 +24,8 @@ internal class Sha256SignatureVerifier( private fun getPubKey(): String? { val expressConfig = apiConfigsManager.getEnvironmentConfig(ApiConfig.ID.Express) return when (expressConfig.environment) { - ApiEnvironment.PROD -> configManager.config.express?.signVerifierPublicKey - else -> configManager.config.devExpress?.signVerifierPublicKey + ApiEnvironment.PROD -> configManager.getConfigSync().express?.signVerifierPublicKey + else -> configManager.getConfigSync().devExpress?.signVerifierPublicKey } } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/ConfigModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/ConfigModule.kt index 4841d4470f..f46bff1f09 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/ConfigModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/ConfigModule.kt @@ -1,16 +1,26 @@ package com.tangem.datasource.di +import com.tangem.datasource.asset.loader.AssetLoader import com.tangem.datasource.config.ConfigManager -import com.tangem.datasource.config.ConfigManagerImpl -import dagger.Binds +import com.tangem.datasource.config.DefaultConfigManager +import com.tangem.datasource.config.models.Config +import com.tangem.datasource.local.datastore.RuntimeStateStore import dagger.Module +import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -internal interface ConfigModule { +internal object ConfigModule { - @Binds - fun bindConfigManager(configManager: ConfigManagerImpl): ConfigManager + @Provides + @Singleton + fun providesConfigManager(assetLoader: AssetLoader): ConfigManager { + return DefaultConfigManager( + assetLoader = assetLoader, + configStore = RuntimeStateStore(defaultValue = Config()), + ) + } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/FileDataStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/FileDataStore.kt deleted file mode 100644 index f67f25584f..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/FileDataStore.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.tangem.datasource.local.datastore - -import com.squareup.moshi.JsonAdapter -import com.tangem.datasource.files.FileReader -import com.tangem.datasource.local.datastore.core.StringKeyDataStore -import com.tangem.datasource.local.datastore.utils.Trigger -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import timber.log.Timber - -@Deprecated("Use shared preferences data store instead") -internal class FileDataStore( - private val fileReader: FileReader, - private val adapter: JsonAdapter, -) : StringKeyDataStore { - - private val mutex = Mutex() - private val writeTrigger = Trigger() - override suspend fun isEmpty(): Boolean { - val e = NotImplementedError("`isEmpty()` function not implemented for `FileDataStore`") - Timber.e(e) - - throw e - } - - override suspend fun contains(key: String): Boolean = getSyncOrNull(key) != null - - override fun get(key: String): Flow { - return writeTrigger - .map { - mutex.withLock { - getInternal(key) - } - } - .filterNotNull() - .distinctUntilChanged() - } - - override fun getAll(): Flow> { - val e = NotImplementedError("`getAll()` function not implemented for `FileDataStore`") - Timber.e(e) - - throw e - } - - override suspend fun getSyncOrNull(key: String): Value? { - return getInternal(key) - } - - override suspend fun getAllSyncOrNull(): List? { - val e = NotImplementedError("`getAllSyncOrNull()` function not implemented for `FileDataStore`") - Timber.e(e) - - throw e - } - - override suspend fun store(key: String, value: Value) { - try { - mutex.withLock { - val json = adapter.toJson(value) - - fileReader.rewriteFile(json, key) - writeTrigger.trigger() - } - } catch (e: Throwable) { - Timber.e(e, "Unable to write file: $key") - } - } - - override suspend fun store(values: Map) { - values.forEach { (key, item) -> - store(key, item) - } - } - - override suspend fun remove(key: String) { - fileReader.removeFile(key) - writeTrigger.trigger() - } - - override suspend fun remove(keys: Collection) { - val e = NotImplementedError("`remove(keys)` function not implemented for `FileDataStore`") - Timber.e(e) - - throw e - } - - override suspend fun clear() { - val e = NotImplementedError("`clear()` function not implemented for `FileDataStore`") - Timber.e(e) - - throw e - } - - private fun getInternal(fileName: String): Value? { - return try { - val json = fileReader.readFile(fileName) - - adapter.fromJson(json) - } catch (e: Throwable) { - Timber.e(e, "Unable to read file: $fileName") - null - } - } -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/RuntimeStateStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/RuntimeStateStore.kt new file mode 100644 index 0000000000..428169c98e --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/RuntimeStateStore.kt @@ -0,0 +1,37 @@ +package com.tangem.datasource.local.datastore + +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +/** + * Runtime store + * +[REDACTED_AUTHOR] + */ +interface RuntimeStateStore { + + /** Get flow of elements [T] */ + fun get(): StateFlow + + /** Store [value] */ + suspend fun store(value: T) + + companion object { + + /** + * Create [RuntimeStateStore] + * + * @param T type of stored value + * @param defaultValue default value + */ + operator fun invoke(defaultValue: T): RuntimeStateStore = object : RuntimeStateStore { + private val flow = MutableStateFlow(value = defaultValue) + + override fun get(): StateFlow = flow + + override suspend fun store(value: T) { + flow.value = value + } + } + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/utils/Trigger.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/utils/Trigger.kt deleted file mode 100644 index d343129a6f..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/utils/Trigger.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.tangem.datasource.local.datastore.utils - -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.FlowCollector -import kotlinx.coroutines.flow.MutableStateFlow - -/** - * Represents a trigger mechanism to emit values on-demand. - * - * This class provides a mechanism to trigger emissions via the [trigger] method. - * - * @property triggerFlow The internal flow that gets toggled to trigger emissions. - */ -internal class Trigger( - private val triggerFlow: MutableStateFlow = MutableStateFlow(value = false), -) : Flow { - - /** - * Collects values emitted by this flow. - * - * Overrides the default collection mechanism to emit a [Unit] value whenever [triggerFlow] changes. - * - * @param collector The collector responsible for handling emitted values. - */ - override suspend fun collect(collector: FlowCollector): Nothing { - triggerFlow.collect { collector.emit(Unit) } - } - - /** - * Triggers an emission. - */ - fun trigger() { - triggerFlow.value = !triggerFlow.value - } -} \ No newline at end of file diff --git a/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/MockConfigManager.kt b/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/MockConfigManager.kt index 1d8ed73429..294c72030a 100644 --- a/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/MockConfigManager.kt +++ b/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/MockConfigManager.kt @@ -1,10 +1,9 @@ package com.tangem.datasource.api.common.config.managers import com.tangem.datasource.config.ConfigManager -import com.tangem.datasource.config.Loader import com.tangem.datasource.config.models.Config -import com.tangem.datasource.config.models.ConfigModel import com.tangem.datasource.config.models.ExpressModel +import kotlinx.coroutines.flow.flowOf /** * Mock [ConfigManager] implementation for [ProdApiConfigsManagerTest] @@ -13,10 +12,17 @@ import com.tangem.datasource.config.models.ExpressModel */ internal class MockConfigManager : ConfigManager { - override val config = Config( - express = ExpressModel(apiKey = ProdApiConfigsManagerTest.EXPRESS_API_KEY, signVerifierPublicKey = ""), - devExpress = ExpressModel(apiKey = ProdApiConfigsManagerTest.EXPRESS_DEV_API_KEY, signVerifierPublicKey = ""), + private val config = Config( + express = ExpressModel(apiKey = EXPRESS_API_KEY, signVerifierPublicKey = "vocibus"), + devExpress = ExpressModel(apiKey = EXPRESS_DEV_API_KEY, signVerifierPublicKey = "pellentesque"), ) - override suspend fun load(configLoader: Loader, onComplete: ((config: Config) -> Unit)?) = Unit + override suspend fun initialize() = config + override fun getConfig() = flowOf(config) + override fun getConfigSync() = config + + companion object { + const val EXPRESS_API_KEY = "express_api_key" + const val EXPRESS_DEV_API_KEY = "express_dev_api_key" + } } \ No newline at end of file diff --git a/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/ProdApiConfigsManagerTest.kt b/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/ProdApiConfigsManagerTest.kt index d818bd6c9f..96088cf50f 100644 --- a/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/ProdApiConfigsManagerTest.kt +++ b/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/ProdApiConfigsManagerTest.kt @@ -65,8 +65,6 @@ internal class ProdApiConfigsManagerTest(private val model: Model) { const val VERSION_NAME = "debug" const val EXPRESS_USER_ID = "express_user_id" const val EXPRESS_SESSION_ID = "express_session_id" - const val EXPRESS_API_KEY = "express_api_key" - const val EXPRESS_DEV_API_KEY = "express_dev_api_key" const val STAKE_KIT_API_KEY = "stake_kit_api_key" @JvmStatic @@ -107,7 +105,11 @@ internal class ProdApiConfigsManagerTest(private val model: Model) { }, headers = mapOf( "api-key" to Provider { - if (environment == ApiEnvironment.PROD) EXPRESS_API_KEY else EXPRESS_DEV_API_KEY + if (environment == ApiEnvironment.PROD) { + MockConfigManager.EXPRESS_API_KEY + } else { + MockConfigManager.EXPRESS_DEV_API_KEY + } }, "user-id" to Provider { EXPRESS_USER_ID }, "session-id" to Provider { EXPRESS_SESSION_ID },