Updated on 2026-08-14

This commit is contained in:
Tangem 2024-09-26 10:28:45 +03:00
parent 8f5db97d2f
commit 801a02506f
19 changed files with 255 additions and 338 deletions

View file

@ -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<AppState> {
@ -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)

View file

@ -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? = {

View file

@ -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")
}
}

View file

@ -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")

View file

@ -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<ConfigModel>, onComplete: ((config: Config) -> Unit)? = null)
/** Get [Config] as [Flow] */
fun getConfig(): Flow<Config>
/** Get [Config] synchronously */
fun getConfigSync(): Config
}

View file

@ -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<ConfigModel>, 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),
)
}
}
}

View file

@ -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<Config>,
) : ConfigManager {
override suspend fun initialize(): Config {
val configValueModel = assetLoader.load<ConfigValueModel>(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<Config> = configStore.get()
override fun getConfigSync(): Config = configStore.get().value
private companion object {
const val CONFIG_FILE_NAME = "tangem-app-config/config_${BuildConfig.ENVIRONMENT}"
}
}

View file

@ -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<ConfigModel> {
private val configValuesName = "tangem-app-config/config_$buildEnvironment"
override suspend fun load(onComplete: (ConfigModel) -> Unit) {
ConfigModel(
configValues = assetLoader.load<ConfigValueModel>(fileName = configValuesName),
).also(onComplete)
}
}

View file

@ -1,8 +0,0 @@
package com.tangem.datasource.config
/**
[REDACTED_AUTHOR]
*/
interface Loader<T> {
suspend fun load(onComplete: (T) -> Unit)
}

View file

@ -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<ConfigValueModel, BlockchainSdkConfig> {
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),
)
}
}
}

View file

@ -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<ConfigValueModel, Config> {
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,
)
}
}

View file

@ -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)
}
}
)

View file

@ -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
}
}
}

View file

@ -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()),
)
}
}

View file

@ -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<Value : Any>(
private val fileReader: FileReader,
private val adapter: JsonAdapter<Value>,
) : StringKeyDataStore<Value> {
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<Value> {
return writeTrigger
.map {
mutex.withLock {
getInternal(key)
}
}
.filterNotNull()
.distinctUntilChanged()
}
override fun getAll(): Flow<List<Value>> {
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<Value>? {
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<String, Value>) {
values.forEach { (key, item) ->
store(key, item)
}
}
override suspend fun remove(key: String) {
fileReader.removeFile(key)
writeTrigger.trigger()
}
override suspend fun remove(keys: Collection<String>) {
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
}
}
}

View file

@ -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<T> {
/** Get flow of elements [T] */
fun get(): StateFlow<T>
/** Store [value] */
suspend fun store(value: T)
companion object {
/**
* Create [RuntimeStateStore]
*
* @param T type of stored value
* @param defaultValue default value
*/
operator fun <T> invoke(defaultValue: T): RuntimeStateStore<T> = object : RuntimeStateStore<T> {
private val flow = MutableStateFlow(value = defaultValue)
override fun get(): StateFlow<T> = flow
override suspend fun store(value: T) {
flow.value = value
}
}
}
}

View file

@ -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<Boolean> = MutableStateFlow(value = false),
) : Flow<Unit> {
/**
* 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<Unit>): Nothing {
triggerFlow.collect { collector.emit(Unit) }
}
/**
* Triggers an emission.
*/
fun trigger() {
triggerFlow.value = !triggerFlow.value
}
}

View file

@ -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<ConfigModel>, 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"
}
}

View file

@ -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 },