Updated on 2026-08-14
This commit is contained in:
parent
4d1902af26
commit
bf4a7a9e4e
7 changed files with 891 additions and 5 deletions
|
|
@ -1,4 +1,6 @@
|
||||||
|
import com.tangem.plugin.configuration.configurations.EnvironmentConfigGenerator
|
||||||
import com.tangem.plugin.configuration.configurations.extension.kaptForObfuscatingVariants
|
import com.tangem.plugin.configuration.configurations.extension.kaptForObfuscatingVariants
|
||||||
|
import com.tangem.plugin.configuration.model.BuildType
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(deps.plugins.android.library)
|
alias(deps.plugins.android.library)
|
||||||
|
|
@ -10,14 +12,53 @@ plugins {
|
||||||
id("configuration")
|
id("configuration")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class GenerateEnvironmentConfigTask : DefaultTask() {
|
||||||
|
|
||||||
|
@get:InputFile
|
||||||
|
abstract val configFile: RegularFileProperty
|
||||||
|
|
||||||
|
@get:OutputDirectory
|
||||||
|
abstract val outputDir: DirectoryProperty
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
fun generate() {
|
||||||
|
val input = configFile.get().asFile
|
||||||
|
require(input.exists()) { "Config file not found: ${input.absolutePath}" }
|
||||||
|
logger.lifecycle("Generating EnvironmentConfig from ${input.name}")
|
||||||
|
EnvironmentConfigGenerator.generate(input, outputDir.get().asFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "com.tangem.datasource"
|
namespace = "com.tangem.datasource"
|
||||||
|
|
||||||
|
sourceSets["main"].java.srcDir(layout.buildDirectory.dir("generated/source/environment-config"))
|
||||||
|
|
||||||
room {
|
room {
|
||||||
schemaDirectory("$projectDir/schemas")
|
schemaDirectory("$projectDir/schemas")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
androidComponents {
|
||||||
|
onVariants { variant ->
|
||||||
|
val buildType = BuildType.values().firstOrNull { it.id == variant.buildType } ?: BuildType.Debug
|
||||||
|
val configFile = rootProject.file(
|
||||||
|
"app/src/main/assets/tangem-app-config/config_${buildType.environment}.json",
|
||||||
|
)
|
||||||
|
|
||||||
|
tasks.register<GenerateEnvironmentConfigTask>(
|
||||||
|
"generateEnvironmentConfig${variant.name.replaceFirstChar { it.uppercaseChar() }}",
|
||||||
|
) {
|
||||||
|
this.configFile.set(configFile)
|
||||||
|
outputDir.set(layout.buildDirectory.dir("generated/source/environment-config"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named("preBuild") {
|
||||||
|
dependsOn(tasks.matching { it.name.startsWith("generateEnvironmentConfig") })
|
||||||
|
}
|
||||||
|
|
||||||
tasks.withType<Test>().configureEach {
|
tasks.withType<Test>().configureEach {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,181 @@
|
||||||
|
package com.tangem.datasource.local.config.environment.converter
|
||||||
|
|
||||||
|
import com.tangem.blockchain.common.*
|
||||||
|
import com.tangem.datasource.local.config.environment.EnvironmentConfig
|
||||||
|
import com.tangem.datasource.local.config.environment.generated.GeneratedEnvironmentConfig
|
||||||
|
import com.tangem.datasource.local.config.environment.generated.GeneratedEnvironmentConfig.AppsFlyer
|
||||||
|
import com.tangem.datasource.local.config.environment.generated.GeneratedEnvironmentConfig.DevExpress
|
||||||
|
import com.tangem.datasource.local.config.environment.generated.GeneratedEnvironmentConfig.Express
|
||||||
|
import com.tangem.datasource.local.config.environment.generated.GeneratedEnvironmentConfig.GetBlockAccessTokens
|
||||||
|
import com.tangem.datasource.local.config.environment.generated.GeneratedEnvironmentConfig.P2pApiKey
|
||||||
|
import com.tangem.datasource.local.config.environment.generated.GeneratedEnvironmentConfig.TonCenterApiKey
|
||||||
|
import com.tangem.datasource.local.config.environment.models.ExpressModel
|
||||||
|
import com.tangem.datasource.local.config.environment.models.P2PKeys
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts [GeneratedEnvironmentConfig] to [EnvironmentConfig]
|
||||||
|
*
|
||||||
|
* This converter maps the auto-generated config (from JSON) to the domain model.
|
||||||
|
* The generated config has nested objects that mirror the JSON structure.
|
||||||
|
*/
|
||||||
|
internal object GeneratedEnvironmentConfigConverter {
|
||||||
|
|
||||||
|
fun convert(): EnvironmentConfig {
|
||||||
|
return EnvironmentConfig(
|
||||||
|
moonPayApiKey = GeneratedEnvironmentConfig.moonPayApiKey,
|
||||||
|
moonPayApiSecretKey = GeneratedEnvironmentConfig.moonPayApiSecretKey,
|
||||||
|
mercuryoWidgetId = GeneratedEnvironmentConfig.mercuryoWidgetId,
|
||||||
|
mercuryoSecret = GeneratedEnvironmentConfig.mercuryoSecret,
|
||||||
|
blockchainSdkConfig = createBlockchainSdkConfig(),
|
||||||
|
amplitudeApiKey = GeneratedEnvironmentConfig.amplitudeApiKey,
|
||||||
|
appsFlyerApiKey = AppsFlyer.appsFlyerDevKey,
|
||||||
|
appsAppId = AppsFlyer.appsFlyerAppID,
|
||||||
|
walletConnectProjectId = GeneratedEnvironmentConfig.walletConnectProjectId,
|
||||||
|
express = createExpressModel(
|
||||||
|
apiKey = Express.apiKey,
|
||||||
|
signVerifierPublicKey = Express.signVerifierPublicKey,
|
||||||
|
),
|
||||||
|
devExpress = createExpressModel(
|
||||||
|
apiKey = DevExpress.apiKey,
|
||||||
|
signVerifierPublicKey = DevExpress.signVerifierPublicKey,
|
||||||
|
),
|
||||||
|
stakeKitApiKey = GeneratedEnvironmentConfig.stakeKitApiKey,
|
||||||
|
p2pApiKey = createP2PKeys(),
|
||||||
|
blockAidApiKey = GeneratedEnvironmentConfig.blockaidApiKey,
|
||||||
|
tangemApiKey = GeneratedEnvironmentConfig.tangemApiKey,
|
||||||
|
tangemApiKeyDev = GeneratedEnvironmentConfig.tangemApiKeyDev,
|
||||||
|
tangemApiKeyStage = GeneratedEnvironmentConfig.tangemApiKeyStage,
|
||||||
|
yieldModuleApiKey = GeneratedEnvironmentConfig.yieldModuleApiKey,
|
||||||
|
yieldModuleApiKeyDev = GeneratedEnvironmentConfig.yieldModuleApiKeyDev,
|
||||||
|
bffStaticToken = GeneratedEnvironmentConfig.bffStaticToken,
|
||||||
|
bffStaticTokenDev = GeneratedEnvironmentConfig.bffStaticTokenDev,
|
||||||
|
gaslessTxApiKeyDev = GeneratedEnvironmentConfig.gaslessTxApiKeyDev,
|
||||||
|
gaslessTxApiKey = GeneratedEnvironmentConfig.gaslessTxApiKey,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createExpressModel(apiKey: String?, signVerifierPublicKey: String?): ExpressModel? {
|
||||||
|
return if (!apiKey.isNullOrEmpty() && !signVerifierPublicKey.isNullOrEmpty()) {
|
||||||
|
ExpressModel(apiKey = apiKey, signVerifierPublicKey = signVerifierPublicKey)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createP2PKeys(): P2PKeys? {
|
||||||
|
val mainnet = P2pApiKey.mainnet
|
||||||
|
val hoodi = P2pApiKey.hoodi
|
||||||
|
return if (mainnet.isNotEmpty() && hoodi.isNotEmpty()) {
|
||||||
|
P2PKeys(mainnet = mainnet, hoodi = hoodi)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createBlockchainSdkConfig(): BlockchainSdkConfig {
|
||||||
|
return BlockchainSdkConfig(
|
||||||
|
blockchairCredentials = BlockchairCredentials(
|
||||||
|
apiKey = GeneratedEnvironmentConfig.blockchairApiKeys,
|
||||||
|
authToken = GeneratedEnvironmentConfig.blockchairAuthorizationToken,
|
||||||
|
),
|
||||||
|
blockcypherTokens = GeneratedEnvironmentConfig.blockcypherTokens.toSet(),
|
||||||
|
quickNodeSolanaCredentials = QuickNodeCredentials(
|
||||||
|
apiKey = GeneratedEnvironmentConfig.quiknodeApiKey,
|
||||||
|
subdomain = GeneratedEnvironmentConfig.quiknodeSubdomain,
|
||||||
|
),
|
||||||
|
quickNodeBscCredentials = QuickNodeCredentials(
|
||||||
|
apiKey = GeneratedEnvironmentConfig.bscQuiknodeApiKey,
|
||||||
|
subdomain = GeneratedEnvironmentConfig.bscQuiknodeSubdomain,
|
||||||
|
),
|
||||||
|
quickNodePlasmaCredentials = QuickNodeCredentials(
|
||||||
|
apiKey = GeneratedEnvironmentConfig.quiknodePlasmaApiKey,
|
||||||
|
subdomain = GeneratedEnvironmentConfig.quiknodePlasmaSubdomain,
|
||||||
|
),
|
||||||
|
quickNodeMonadCredentials = QuickNodeCredentials(
|
||||||
|
apiKey = GeneratedEnvironmentConfig.quiknodeMonadApiKey,
|
||||||
|
subdomain = GeneratedEnvironmentConfig.quiknodeMonadSubdomain,
|
||||||
|
),
|
||||||
|
infuraProjectId = GeneratedEnvironmentConfig.infuraProjectId,
|
||||||
|
tronGridApiKey = GeneratedEnvironmentConfig.tronGridApiKey,
|
||||||
|
nowNodeCredentials = NowNodeCredentials(apiKey = GeneratedEnvironmentConfig.nowNodesApiKey),
|
||||||
|
getBlockCredentials = createGetBlockCredentials(),
|
||||||
|
kaspaSecondaryApiUrl = GeneratedEnvironmentConfig.kaspaSecondaryApiUrl,
|
||||||
|
tonCenterCredentials = TonCenterCredentials(
|
||||||
|
mainnetApiKey = TonCenterApiKey.mainnet,
|
||||||
|
testnetApiKey = TonCenterApiKey.testnet,
|
||||||
|
),
|
||||||
|
chiaFireAcademyApiKey = GeneratedEnvironmentConfig.chiaFireAcademyApiKey,
|
||||||
|
chiaTangemApiKey = GeneratedEnvironmentConfig.chiaTangemApiKey,
|
||||||
|
hederaArkhiaApiKey = GeneratedEnvironmentConfig.hederaArkhiaKey,
|
||||||
|
polygonScanApiKey = GeneratedEnvironmentConfig.polygonScanApiKey,
|
||||||
|
bittensorDwellirApiKey = GeneratedEnvironmentConfig.bittensorDwellirKey,
|
||||||
|
bittensorOnfinalityApiKey = GeneratedEnvironmentConfig.bittensorOnfinalityKey,
|
||||||
|
dwellirApiKey = GeneratedEnvironmentConfig.dwellirApiKey,
|
||||||
|
koinosProApiKey = GeneratedEnvironmentConfig.koinosProApiKey,
|
||||||
|
alephiumApiKey = GeneratedEnvironmentConfig.alephiumTangemApiKey,
|
||||||
|
moralisApiKey = GeneratedEnvironmentConfig.moralisApiKey,
|
||||||
|
etherscanApiKey = GeneratedEnvironmentConfig.etherscanApiKey,
|
||||||
|
blinkApiKey = GeneratedEnvironmentConfig.blinkApiKey,
|
||||||
|
tatumApiKey = GeneratedEnvironmentConfig.tatumApiKey,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createGetBlockCredentials(): GetBlockCredentials {
|
||||||
|
return GetBlockCredentials(
|
||||||
|
xrp = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Xrp.jsonRpc),
|
||||||
|
cardano = GetBlockAccessToken(rosetta = GetBlockAccessTokens.Cardano.rosetta),
|
||||||
|
avalanche = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Avalanche.jsonRpc),
|
||||||
|
eth = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Ethereum.jsonRpc),
|
||||||
|
etc = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.EthereumClassic.jsonRpc),
|
||||||
|
fantom = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Fantom.jsonRpc),
|
||||||
|
rsk = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Rsk.jsonRpc),
|
||||||
|
bsc = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Bsc.jsonRpc),
|
||||||
|
polygon = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Polygon.jsonRpc),
|
||||||
|
gnosis = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Xdai.jsonRpc),
|
||||||
|
cronos = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Cronos.jsonRpc),
|
||||||
|
solana = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Solana.jsonRpc),
|
||||||
|
ton = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Ton.jsonRpc),
|
||||||
|
tron = GetBlockAccessToken(rest = GetBlockAccessTokens.Tron.rest),
|
||||||
|
cosmos = GetBlockAccessToken(rest = GetBlockAccessTokens.CosmosHub.rest),
|
||||||
|
near = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Near.jsonRpc),
|
||||||
|
aptos = GetBlockAccessToken(rest = GetBlockAccessTokens.Aptos.rest),
|
||||||
|
dogecoin = GetBlockAccessToken(
|
||||||
|
jsonRpc = GetBlockAccessTokens.Dogecoin.jsonRpc,
|
||||||
|
blockBookRest = GetBlockAccessTokens.Dogecoin.blockBookRest,
|
||||||
|
),
|
||||||
|
litecoin = GetBlockAccessToken(
|
||||||
|
jsonRpc = GetBlockAccessTokens.Litecoin.jsonRpc,
|
||||||
|
blockBookRest = GetBlockAccessTokens.Litecoin.blockBookRest,
|
||||||
|
),
|
||||||
|
dash = GetBlockAccessToken(
|
||||||
|
jsonRpc = GetBlockAccessTokens.Dash.jsonRpc,
|
||||||
|
blockBookRest = GetBlockAccessTokens.Dash.blockBookRest,
|
||||||
|
),
|
||||||
|
bitcoin = GetBlockAccessToken(
|
||||||
|
jsonRpc = GetBlockAccessTokens.Bitcoin.jsonRpc,
|
||||||
|
blockBookRest = GetBlockAccessTokens.Bitcoin.blockBookRest,
|
||||||
|
),
|
||||||
|
algorand = GetBlockAccessToken(rest = GetBlockAccessTokens.Algorand.rest),
|
||||||
|
zkSyncEra = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Zksync.jsonRpc),
|
||||||
|
polygonZkEvm = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.PolygonZkevm.jsonRpc),
|
||||||
|
base = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Base.jsonRpc),
|
||||||
|
blast = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Blast.jsonRpc),
|
||||||
|
filecoin = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Filecoin.jsonRpc),
|
||||||
|
arbitrum = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.ArbitrumOne.jsonRpc),
|
||||||
|
bitcoinCash = GetBlockAccessToken(
|
||||||
|
jsonRpc = GetBlockAccessTokens.BitcoinCash.jsonRpc,
|
||||||
|
blockBookRest = GetBlockAccessTokens.BitcoinCash.blockBookRest,
|
||||||
|
),
|
||||||
|
kusama = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Kusama.jsonRpc),
|
||||||
|
moonbeam = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Moonbeam.jsonRpc),
|
||||||
|
optimism = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Optimism.jsonRpc),
|
||||||
|
polkadot = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Polkadot.jsonRpc),
|
||||||
|
shibarium = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Shibarium.jsonRpc),
|
||||||
|
sui = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Sui.jsonRpc),
|
||||||
|
telos = GetBlockAccessToken(jsonRpc = GetBlockAccessTokens.Telos.jsonRpc),
|
||||||
|
tezos = GetBlockAccessToken(rest = GetBlockAccessTokens.Tezos.rest),
|
||||||
|
monad = GetBlockAccessToken(rest = GetBlockAccessTokens.Monad.rest),
|
||||||
|
stellar = GetBlockAccessToken(rest = GetBlockAccessTokens.Stellar.rest),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -102,6 +102,7 @@ markdownComposeView = "0.5.4"
|
||||||
usedesk = "4.4.0"
|
usedesk = "4.4.0"
|
||||||
sumsub = "1.38.0"
|
sumsub = "1.38.0"
|
||||||
haze = "1.7.1"
|
haze = "1.7.1"
|
||||||
|
kotlinpoet = "1.18.1"
|
||||||
# endregion Other libraries
|
# endregion Other libraries
|
||||||
|
|
||||||
# region Tools
|
# region Tools
|
||||||
|
|
@ -149,6 +150,7 @@ agconnect = { id = "com.huawei.agconnect", version.ref = "agconnect" }
|
||||||
gradle-android = { module = "com.android.tools.build:gradle", version.ref = "androidGradlePlugin" }
|
gradle-android = { module = "com.android.tools.build:gradle", version.ref = "androidGradlePlugin" }
|
||||||
gradle-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
gradle-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
||||||
gradle-detekt = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" }
|
gradle-detekt = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" }
|
||||||
|
gradle-kotlinpoet = { module = "com.squareup:kotlinpoet", version.ref = "kotlinpoet" }
|
||||||
# end region Classpath
|
# end region Classpath
|
||||||
|
|
||||||
# region AndroidX
|
# region AndroidX
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,15 @@ dependencies {
|
||||||
implementation(deps.gradle.kotlin)
|
implementation(deps.gradle.kotlin)
|
||||||
implementation(deps.gradle.android)
|
implementation(deps.gradle.android)
|
||||||
implementation(deps.gradle.detekt)
|
implementation(deps.gradle.detekt)
|
||||||
|
implementation(deps.gradle.kotlinpoet)
|
||||||
|
implementation(deps.kotlin.serialization)
|
||||||
|
|
||||||
|
testImplementation(deps.test.junit5)
|
||||||
|
testImplementation(deps.test.truth)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType<Test> {
|
||||||
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
|
||||||
gradlePlugin {
|
gradlePlugin {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,165 @@
|
||||||
|
package com.tangem.plugin.configuration.configurations
|
||||||
|
|
||||||
|
import com.squareup.kotlinpoet.*
|
||||||
|
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
|
||||||
|
import kotlinx.serialization.json.*
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generator for environment configuration Kotlin object from JSON file.
|
||||||
|
* Automatically parses JSON structure and generates corresponding Kotlin code.
|
||||||
|
*
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
object EnvironmentConfigGenerator {
|
||||||
|
|
||||||
|
private const val PACKAGE_NAME = "com.tangem.datasource.local.config.environment.generated"
|
||||||
|
private const val CLASS_NAME = "GeneratedEnvironmentConfig"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates GeneratedEnvironmentConfig object from JSON file.
|
||||||
|
*
|
||||||
|
* @param inputFile JSON configuration file
|
||||||
|
* @param outputDir Output directory for generated Kotlin file
|
||||||
|
*/
|
||||||
|
fun generate(inputFile: File, outputDir: File) {
|
||||||
|
val jsonText = inputFile.readText()
|
||||||
|
val json = Json.parseToJsonElement(jsonText).jsonObject
|
||||||
|
|
||||||
|
val objectBuilder = TypeSpec.objectBuilder(CLASS_NAME)
|
||||||
|
.addKdoc("Generated from ${inputFile.name}\nAuto-generated - do not edit manually.")
|
||||||
|
|
||||||
|
// Iterate over all JSON keys and generate properties
|
||||||
|
json.entries.forEach { (key, value) ->
|
||||||
|
addPropertyFromJsonValue(objectBuilder, key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
val fileSpec = FileSpec.builder(PACKAGE_NAME, CLASS_NAME)
|
||||||
|
.indent(" ") // Use 4 spaces for indentation
|
||||||
|
.addType(objectBuilder.build())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
outputDir.mkdirs()
|
||||||
|
fileSpec.writeTo(outputDir)
|
||||||
|
|
||||||
|
// Post-process generated file
|
||||||
|
val generatedFile = File(outputDir, PACKAGE_NAME.replace('.', '/') + "/$CLASS_NAME.kt")
|
||||||
|
if (generatedFile.exists()) {
|
||||||
|
val content = generatedFile.readText()
|
||||||
|
val fixedContent = content
|
||||||
|
// Add suppress annotation at file level
|
||||||
|
.replaceFirst(
|
||||||
|
"package $PACKAGE_NAME",
|
||||||
|
"@file:Suppress(\n" +
|
||||||
|
" \"MaximumLineLength\",\n" +
|
||||||
|
" \"MaxLineLength\",\n" +
|
||||||
|
" \"Indentation\",\n" +
|
||||||
|
")\n\npackage $PACKAGE_NAME"
|
||||||
|
)
|
||||||
|
// Remove redundant public modifiers
|
||||||
|
.replace("public object ", "object ")
|
||||||
|
.replace("public val ", "val ")
|
||||||
|
.replace("public const val ", "const val ")
|
||||||
|
generatedFile.writeText(fixedContent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a property to the TypeSpec based on the JSON value type
|
||||||
|
*/
|
||||||
|
private fun addPropertyFromJsonValue(builder: TypeSpec.Builder, name: String, value: JsonElement) {
|
||||||
|
when (value) {
|
||||||
|
is JsonPrimitive -> {
|
||||||
|
when {
|
||||||
|
value.isString -> {
|
||||||
|
val stringValue = value.content
|
||||||
|
val isNullable = stringValue.isEmpty()
|
||||||
|
val propertySpec = PropertySpec.builder(name, STRING.copy(nullable = isNullable))
|
||||||
|
.initializer(if (isNullable) "null" else "%S", stringValue)
|
||||||
|
|
||||||
|
// Add const modifier for non-nullable strings
|
||||||
|
if (!isNullable) {
|
||||||
|
propertySpec.addModifiers(KModifier.CONST)
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.addProperty(propertySpec.build())
|
||||||
|
}
|
||||||
|
value.booleanOrNull != null -> {
|
||||||
|
builder.addProperty(
|
||||||
|
PropertySpec.builder(name, BOOLEAN)
|
||||||
|
.addModifiers(KModifier.CONST)
|
||||||
|
.initializer("%L", value.boolean)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
value.longOrNull != null -> {
|
||||||
|
builder.addProperty(
|
||||||
|
PropertySpec.builder(name, LONG)
|
||||||
|
.addModifiers(KModifier.CONST)
|
||||||
|
.initializer("%L", value.long)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
value.doubleOrNull != null -> {
|
||||||
|
builder.addProperty(
|
||||||
|
PropertySpec.builder(name, DOUBLE)
|
||||||
|
.addModifiers(KModifier.CONST)
|
||||||
|
.initializer("%L", value.double)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// Null value
|
||||||
|
builder.addProperty(
|
||||||
|
PropertySpec.builder(name, STRING.copy(nullable = true))
|
||||||
|
.initializer("null")
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is JsonArray -> {
|
||||||
|
val listType = LIST.parameterizedBy(STRING)
|
||||||
|
val values = value.map { it.jsonPrimitive.content }
|
||||||
|
builder.addProperty(
|
||||||
|
PropertySpec.builder(name, listType)
|
||||||
|
.initializer(
|
||||||
|
CodeBlock.builder()
|
||||||
|
.add("listOf(\n")
|
||||||
|
.apply {
|
||||||
|
values.forEach { v ->
|
||||||
|
add(" %S,\n", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.add(")")
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is JsonObject -> {
|
||||||
|
// Generate nested object with proper naming (convert dashes to camelCase)
|
||||||
|
val nestedClassName = name.toPascalCase()
|
||||||
|
val nestedObjectBuilder = TypeSpec.objectBuilder(nestedClassName)
|
||||||
|
|
||||||
|
value.entries.forEach { (nestedKey, nestedValue) ->
|
||||||
|
addPropertyFromJsonValue(nestedObjectBuilder, nestedKey, nestedValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.addType(nestedObjectBuilder.build())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a string to PascalCase, handling dashes and underscores.
|
||||||
|
* Examples: "cosmos-hub" -> "CosmosHub", "polygon-zkevm" -> "PolygonZkevm"
|
||||||
|
*/
|
||||||
|
private fun String.toPascalCase(): String {
|
||||||
|
return this.split("-", "_")
|
||||||
|
.filter { it.isNotEmpty() }
|
||||||
|
.joinToString("") { part ->
|
||||||
|
part.replaceFirstChar { it.uppercase() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
package com.tangem.plugin.configuration.model
|
package com.tangem.plugin.configuration.model
|
||||||
|
|
||||||
internal enum class BuildType(
|
enum class BuildType(
|
||||||
val id: String,
|
val id: String,
|
||||||
val appIdSuffix: String? = null,
|
internal val appIdSuffix: String? = null,
|
||||||
val versionSuffix: String? = null,
|
internal val versionSuffix: String? = null,
|
||||||
val obfuscating: Boolean = false,
|
internal val obfuscating: Boolean = false,
|
||||||
val configFields: List<BuildConfigField>,
|
internal val configFields: List<BuildConfigField>,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -117,4 +117,19 @@ internal enum class BuildType(
|
||||||
BuildConfigField.ABTestsEnabled(isEnabled = false),
|
BuildConfigField.ABTestsEnabled(isEnabled = false),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
;
|
||||||
|
|
||||||
|
/** Returns the environment value (dev/prod) for this build type */
|
||||||
|
val environment: String
|
||||||
|
get() {
|
||||||
|
val environmentField = configFields
|
||||||
|
.filterIsInstance<BuildConfigField.Environment>()
|
||||||
|
.firstOrNull()
|
||||||
|
|
||||||
|
requireNotNull(environmentField) {
|
||||||
|
"BuildType '$id' must have a BuildConfigField.Environment in configFields"
|
||||||
|
}
|
||||||
|
|
||||||
|
return environmentField.value.removeSurrounding("\"")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,473 @@
|
||||||
|
package com.tangem.plugin.configuration.configurations
|
||||||
|
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.io.TempDir
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for [EnvironmentConfigGenerator] covering JSON parsing edge cases.
|
||||||
|
*/
|
||||||
|
class EnvironmentConfigGeneratorTest {
|
||||||
|
|
||||||
|
@TempDir
|
||||||
|
lateinit var tempDir: File
|
||||||
|
|
||||||
|
private lateinit var outputDir: File
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun setup() {
|
||||||
|
outputDir = File(tempDir, "output")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate handles string values correctly`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"apiKey": "test-api-key",
|
||||||
|
"baseUrl": "https://example.com"
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(generatedCode).contains("""const val apiKey: String = "test-api-key"""")
|
||||||
|
assertThat(generatedCode).contains("""const val baseUrl: String = "https://example.com"""")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate handles empty string as nullable`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"emptyValue": ""
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(generatedCode).contains("val emptyValue: String? = null")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate handles null values`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"nullValue": null
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(generatedCode).contains("val nullValue: String? = null")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate handles boolean values`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"isEnabled": true,
|
||||||
|
"isDisabled": false
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(generatedCode).contains("const val isEnabled: Boolean = true")
|
||||||
|
assertThat(generatedCode).contains("const val isDisabled: Boolean = false")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate handles integer values as Long`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"count": 42,
|
||||||
|
"negativeNumber": -100,
|
||||||
|
"largeNumber": 9223372036854775807
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(generatedCode).contains("const val count: Long = 42")
|
||||||
|
assertThat(generatedCode).contains("const val negativeNumber: Long = -100")
|
||||||
|
// KotlinPoet formats large numbers with underscores
|
||||||
|
assertThat(generatedCode).contains("const val largeNumber: Long = 9_223_372_036_854_775_807")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate handles double values`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"ratio": 3.14,
|
||||||
|
"negativeDouble": -2.5
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(generatedCode).contains("const val ratio: Double = 3.14")
|
||||||
|
assertThat(generatedCode).contains("const val negativeDouble: Double = -2.5")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate handles string arrays`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"items": ["one", "two", "three"]
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(generatedCode).contains("val items: List<String> = listOf(")
|
||||||
|
assertThat(generatedCode).contains(""""one",""")
|
||||||
|
assertThat(generatedCode).contains(""""two",""")
|
||||||
|
assertThat(generatedCode).contains(""""three",""")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate handles empty arrays`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"emptyList": []
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(generatedCode).contains("val emptyList: List<String> = listOf(")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate handles nested objects`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"database": {
|
||||||
|
"host": "localhost",
|
||||||
|
"port": 5432
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(generatedCode).contains("object Database {")
|
||||||
|
assertThat(generatedCode).contains("""const val host: String = "localhost"""")
|
||||||
|
// KotlinPoet formats numbers >= 1000 with underscores
|
||||||
|
assertThat(generatedCode).contains("const val port: Long = 5_432")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate handles deeply nested objects`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"level1": {
|
||||||
|
"level2": {
|
||||||
|
"level3": {
|
||||||
|
"deepValue": "deep"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(generatedCode).contains("object Level1 {")
|
||||||
|
assertThat(generatedCode).contains("object Level2 {")
|
||||||
|
assertThat(generatedCode).contains("object Level3 {")
|
||||||
|
assertThat(generatedCode).contains("""const val deepValue: String = "deep"""")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate converts dash-separated names to PascalCase`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"cosmos-hub": {
|
||||||
|
"chainId": "cosmoshub-4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(generatedCode).contains("object CosmosHub {")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate converts underscore-separated names to PascalCase`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"api_config": {
|
||||||
|
"timeout": 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(generatedCode).contains("object ApiConfig {")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate handles consecutive dashes in names`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"cosmos--hub": {
|
||||||
|
"testValue": "test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(generatedCode).contains("object CosmosHub {")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate handles trailing dash in names`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"config-": {
|
||||||
|
"testValue": "test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(generatedCode).contains("object Config {")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate handles special characters in string values`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"query": "SELECT * FROM users WHERE name = 'John'",
|
||||||
|
"path": "C:\\Users\\test",
|
||||||
|
"newline": "line1\nline2",
|
||||||
|
"unicode": "Hello 世界"
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(generatedCode).contains("const val query: String")
|
||||||
|
assertThat(generatedCode).contains("const val path: String")
|
||||||
|
assertThat(generatedCode).contains("const val newline: String")
|
||||||
|
assertThat(generatedCode).contains("const val unicode: String")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate handles arrays with special characters`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"urls": [
|
||||||
|
"https://api.example.com/v1",
|
||||||
|
"https://api.example.com/v2?key=value&other=1"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(generatedCode).contains("val urls: List<String> = listOf(")
|
||||||
|
assertThat(generatedCode).contains(""""https://api.example.com/v1",""")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate adds file suppress annotations`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"key": "value"
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(generatedCode).contains("@file:Suppress(")
|
||||||
|
assertThat(generatedCode).contains(""""MaximumLineLength"""")
|
||||||
|
assertThat(generatedCode).contains(""""MaxLineLength"""")
|
||||||
|
assertThat(generatedCode).contains(""""Indentation"""")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate creates proper package declaration`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"key": "value"
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(generatedCode).contains("package com.tangem.datasource.local.config.environment.generated")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate creates object with correct name`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"key": "value"
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(generatedCode).contains("object GeneratedEnvironmentConfig {")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate adds kdoc with source file reference`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"key": "value"
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(generatedCode).contains("Generated from")
|
||||||
|
assertThat(generatedCode).contains("Auto-generated - do not edit manually")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate removes public modifiers`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"key": "value"
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(generatedCode).doesNotContain("public object")
|
||||||
|
assertThat(generatedCode).doesNotContain("public val")
|
||||||
|
assertThat(generatedCode).doesNotContain("public const val")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate handles complex real-world config`() {
|
||||||
|
// Arrange
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"tangemComApiKey": "api-key-123",
|
||||||
|
"moonPayApiKey": "moon-pay-key",
|
||||||
|
"moonPayApiSecretKey": "secret-key",
|
||||||
|
"mercuryoWidgetId": "",
|
||||||
|
"blockchainSdkConfig": {
|
||||||
|
"blockchairApiKey": "blockchair-key",
|
||||||
|
"blockcypherTokens": ["token1", "token2"],
|
||||||
|
"quickNodeSolanaCredentials": {
|
||||||
|
"apiKey": "solana-key",
|
||||||
|
"subdomain": "solana-node"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isFeatureEnabled": true,
|
||||||
|
"maxRetryCount": 3
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedCode = generateAndReadOutput(json)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// Top-level properties
|
||||||
|
assertThat(generatedCode).contains("""const val tangemComApiKey: String = "api-key-123"""")
|
||||||
|
assertThat(generatedCode).contains("val mercuryoWidgetId: String? = null")
|
||||||
|
assertThat(generatedCode).contains("const val isFeatureEnabled: Boolean = true")
|
||||||
|
assertThat(generatedCode).contains("const val maxRetryCount: Long = 3")
|
||||||
|
|
||||||
|
// Nested object
|
||||||
|
assertThat(generatedCode).contains("object BlockchainSdkConfig {")
|
||||||
|
assertThat(generatedCode).contains("""const val blockchairApiKey: String = "blockchair-key"""")
|
||||||
|
assertThat(generatedCode).contains("val blockcypherTokens: List<String>")
|
||||||
|
|
||||||
|
// Deeply nested object
|
||||||
|
assertThat(generatedCode).contains("object QuickNodeSolanaCredentials {")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateAndReadOutput(jsonContent: String): String {
|
||||||
|
val inputFile = File(tempDir, "config.json").apply {
|
||||||
|
writeText(jsonContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
EnvironmentConfigGenerator.generate(inputFile, outputDir)
|
||||||
|
|
||||||
|
val generatedFile = File(
|
||||||
|
outputDir,
|
||||||
|
"com/tangem/datasource/local/config/environment/generated/GeneratedEnvironmentConfig.kt"
|
||||||
|
)
|
||||||
|
|
||||||
|
assertThat(generatedFile.exists()).isTrue()
|
||||||
|
return generatedFile.readText()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue