Skip to content

Commit

Permalink
Refactor PakkuApi, add documentation & update tests
Browse files Browse the repository at this point in the history
  • Loading branch information
juraj-hrivnak committed Jan 28, 2025
1 parent a5c9a69 commit 88990e3
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 82 deletions.
35 changes: 17 additions & 18 deletions src/commonMain/kotlin/teksturepako/pakku/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import com.github.ajalt.mordant.terminal.Terminal
import com.github.michaelbull.result.get
import com.github.michaelbull.result.onFailure
import kotlinx.coroutines.runBlocking
import teksturepako.pakku.api.executePakku
import teksturepako.pakku.api.initPakku
import teksturepako.pakku.api.http.client
import teksturepako.pakku.api.pakku
import teksturepako.pakku.api.platforms.CURSEFORGE_API_KEY
import teksturepako.pakku.api.platforms.CurseForge
import teksturepako.pakku.api.platforms.Modrinth
Expand All @@ -23,31 +23,30 @@ fun main(args: Array<String>)
{
println()

initPakku {
pakku {
curseForge(apiKey = System.getenv("CURSEFORGE_API_KEY") ?: CURSEFORGE_API_KEY)
withUserAgent("Pakku/$VERSION (github.com/juraj-hrivnak/Pakku)")
}

executePakku {
// Read 'cli-config.json'
val cliConfig = runBlocking { CliConfig.readToResult() }
.onFailure { error -> debug { println(error.rawMessage) } }
.get()
// Read 'cli-config.json'
val cliConfig = runBlocking { CliConfig.readToResult() }
.onFailure { error -> debug { println(error.rawMessage) } }
.get()

Pakku().context {
terminal = cliConfig?.toTerminal() ?: Terminal(theme = CliThemes.Default)
}.subcommands(
Init(), Import(), Add(), Rm(), Cfg(), Set(), Status(), Update(), Ls(), Fetch(), Sync(), Link(), Export(),
Diff()
).main(args)
Pakku().context {
terminal = cliConfig?.toTerminal() ?: Terminal(theme = CliThemes.Default)
}.subcommands(
Init(), Import(), Add(), Rm(), Cfg(), Set(), Status(), Update(), Ls(), Fetch(), Sync(), Link(), Export(),
Diff()
).main(args)

// Check Modrinth's rate limit
Modrinth.checkRateLimit()
// Check Modrinth's rate limit
Modrinth.checkRateLimit()

debug { CurseForge.checkApiKey() }
}
debug { CurseForge.checkApiKey() }

debug { println("Program arguments: ${args.joinToString()}") }

client.close()
exitProcess(0)
}
68 changes: 36 additions & 32 deletions src/commonMain/kotlin/teksturepako/pakku/api/PakkuApi.kt
Original file line number Diff line number Diff line change
@@ -1,58 +1,58 @@
package teksturepako.pakku.api

import teksturepako.pakku.api.http.client
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes

object PakkuApi
{
data class Configuration(
internal var developmentMode: Boolean = false,
internal var curseForgeApiKey: String? = null,
internal var userAgent: String? = null,
internal var timeout: Duration = 3.minutes,
var developmentMode: Boolean = false,
var curseForgeApiKey: String? = null,
var userAgent: String? = null,
var timeout: Duration = 3.minutes,
)
{
/** Enables development mode for testing purposes. */
fun developmentMode()
{
this.developmentMode = true
}

/** Sets the CurseForge API key for authentication. */
fun curseForge(apiKey: String)
{
this.curseForgeApiKey = apiKey
}

/** Sets the user agent for HTTP requests. */
fun withUserAgent(agent: String)
{
this.userAgent = agent
}

/** Sets the timeout duration for HTTP requests. */
fun withTimeout(timeout: Duration)
{
this.timeout = timeout
}

internal fun init()
{
configuration?.let { config ->
if (developmentMode)
{
println("Pakku is running in development mode")
}
else
{
requireNotNull(config.curseForgeApiKey) { "curseForgeApiKey must be specified" }
requireNotNull(config.userAgent) { "userAgent must be specified" }
}
} ?: throw IllegalStateException("PakkuApi must be configured before use")
if (configuration?.developmentMode == true)
{
println("Pakku is running in development mode")
}
else
{
requireNotNull(configuration?.curseForgeApiKey) { "curseForgeApiKey must be specified" }
requireNotNull(configuration?.userAgent) { "userAgent must be specified" }
}
}
}

var configuration: Configuration? = null
private set
get() = field?.copy()
private var configuration: Configuration? = null

@Throws(IllegalStateException::class)
internal fun configure(block: Configuration.() -> Unit)
{
if (configuration == null)
Expand All @@ -62,21 +62,25 @@ object PakkuApi
}
}

fun isConfigured(): Boolean = configuration != null
}
/** Indicates whether development mode is enabled. */
internal val developmentMode: Boolean
get() = configuration?.developmentMode ?: false

fun initPakku(block: PakkuApi.Configuration.() -> Unit)
{
PakkuApi.configure(block)
/** The CurseForge API key used for authentication. */
internal val curseForgeApiKey: String?
get() = configuration?.curseForgeApiKey

/** The user agent used for HTTP requests. */
internal val userAgent: String?
get() = configuration?.userAgent

/** The timeout duration for HTTP requests. */
internal val timeout: Duration
get() = configuration?.timeout ?: 3.minutes
}

fun executePakku(block: () -> Unit): Boolean
/** Initializes Pakku with the provided configuration. */
fun pakku(block: PakkuApi.Configuration.() -> Unit)
{
if (PakkuApi.isConfigured())
{
block()
client.close()
}

return PakkuApi.isConfigured()
PakkuApi.configure(block)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,26 @@ import io.ktor.client.plugins.*
import io.ktor.client.plugins.contentnegotiation.*
import kotlinx.serialization.json.Json
import teksturepako.pakku.api.PakkuApi
import kotlin.time.Duration.Companion.minutes
import kotlin.time.toJavaDuration

val client = HttpClient(OkHttp) {
install(ContentNegotiation) {
Json
}
install(HttpTimeout) {
val timeout = PakkuApi.configuration?.timeout?.inWholeMilliseconds
val timeout = PakkuApi.timeout.inWholeMilliseconds

socketTimeoutMillis = timeout
requestTimeoutMillis = timeout
connectTimeoutMillis = timeout
}
install(UserAgent) {
PakkuApi.configuration?.userAgent?.let { agent = it }
PakkuApi.userAgent?.let { agent = it }
}
engine {
pipelining = true
config {
val timeout = PakkuApi.configuration?.timeout?.toJavaDuration() ?: 1.minutes.toJavaDuration()
val timeout = PakkuApi.timeout.toJavaDuration()

retryOnConnectionFailure(true)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ object CurseForge : Platform(

private const val API_KEY_HEADER = "x-api-key"

private val apiKey: String? = PakkuApi.configuration?.curseForgeApiKey?.takeIf { it.isNotBlank() }
private val apiKey: String? = PakkuApi.curseForgeApiKey?.takeIf { it.isNotBlank() }

fun checkApiKey()
{
Expand Down
4 changes: 2 additions & 2 deletions src/commonTest/kotlin/teksturepako/pakku/PakkuTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package teksturepako.pakku
import kotlinx.coroutines.runBlocking
import teksturepako.pakku.api.data.generatePakkuId
import teksturepako.pakku.api.data.workingPath
import teksturepako.pakku.api.initPakku
import teksturepako.pakku.api.pakku
import java.nio.file.Path
import kotlin.io.path.*
import kotlin.test.AfterTest
Expand Down Expand Up @@ -41,7 +41,7 @@ open class PakkuTest
@BeforeTest
fun `set-up-test`()
{
initPakku {
pakku {
developmentMode()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,31 +25,6 @@ import kotlin.test.assertNotNull

class CfModpackModelTest : PakkuTest()
{
private val modpackName = "CurseForgeProfileTestModpack"

private val greeneryCfId = 574029
private val greeneryCfFileId = 5913357

private val greeneryProject = Project(
type = ProjectType.MOD,
id = mutableMapOf(CurseForge.serialName to greeneryCfId.toString()),
name = mutableMapOf(CurseForge.serialName to "Greenery\uD83C\uDF3F"),
slug = mutableMapOf(CurseForge.serialName to "greenery"),
files = mutableSetOf(
ProjectFile(
type = CurseForge.serialName,
fileName = "Greenery-1.12.2-7.0.jar",
mcVersions = mutableListOf("1.12.2", "1.12.1", "1.12"),
loaders = mutableListOf("forge"),
id = greeneryCfFileId.toString(),
parentId = greeneryCfId.toString(),
)
)
)

private val mcVersion = "1.12.2"
private val forgeVersion = "xxx.xxx.xxx"

override suspend fun `set-up`()
{
val lockFile = LockFile(
Expand Down Expand Up @@ -77,6 +52,31 @@ class CfModpackModelTest : PakkuTest()
}
}

private val modpackName = "CurseForgeProfileTestModpack"

private val greeneryCfId = 574029
private val greeneryCfFileId = 5913357

private val greeneryProject = Project(
type = ProjectType.MOD,
id = mutableMapOf(CurseForge.serialName to greeneryCfId.toString()),
name = mutableMapOf(CurseForge.serialName to "Greenery\uD83C\uDF3F"),
slug = mutableMapOf(CurseForge.serialName to "greenery"),
files = mutableSetOf(
ProjectFile(
type = CurseForge.serialName,
fileName = "Greenery-1.12.2-7.0.jar",
mcVersions = mutableListOf("1.12.2", "1.12.1", "1.12"),
loaders = mutableListOf("forge"),
id = greeneryCfFileId.toString(),
parentId = greeneryCfId.toString(),
)
)
)

private val mcVersion = "1.12.2"
private val forgeVersion = "xxx.xxx.xxx"

@Test
fun `test cf modpack model in cache`()
{
Expand Down

0 comments on commit 88990e3

Please sign in to comment.