Extend plugin config validation

Cache validation results
Show field-level errors in UI
Enable manual revalidation
Move PluginConfigService and PluginManagementService into PluginService
This commit is contained in:
grimsi
2025-05-19 12:21:42 +02:00
parent 08c41265c8
commit d9fef0f30c
23 changed files with 240 additions and 130 deletions
@@ -2,6 +2,7 @@ package de.grimsi.gameyfin.plugins.directdownload
import de.grimsi.gameyfin.pluginapi.core.ConfigurableGameyfinPlugin
import de.grimsi.gameyfin.pluginapi.core.PluginConfigElement
import de.grimsi.gameyfin.pluginapi.core.PluginConfigValidationResult
import de.grimsi.gameyfin.pluginapi.download.Download
import de.grimsi.gameyfin.pluginapi.download.DownloadProvider
import de.grimsi.gameyfin.pluginapi.download.FileDownload
@@ -26,21 +27,26 @@ class DirectDownloadPlugin(wrapper: PluginWrapper) : ConfigurableGameyfinPlugin(
override val configMetadata: List<PluginConfigElement> = listOf(
PluginConfigElement(
key = "compressionMode",
name = "Compression mode for generated ZIP files (\"none\", \"fast\", \"best\")",
name = "Compression mode for generated ZIP files (\"none\" = default, \"fast\", \"best\")",
description = "Higher compression uses more CPU but saves bandwidth",
)
)
override fun validateConfig(config: Map<String, String?>): Boolean {
return config["compressionMode"]?.let {
try {
CompressionMode.valueOf(it.uppercase())
true
override fun validateConfig(config: Map<String, String?>): PluginConfigValidationResult {
val compressionMode = config["compressionMode"]
if (compressionMode != null) {
return try {
CompressionMode.valueOf(compressionMode.uppercase())
PluginConfigValidationResult.VALID
} catch (_: IllegalArgumentException) {
log.error("Invalid compression mode: $it")
false
PluginConfigValidationResult.INVALID(
mapOf("compressionMode" to "Invalid compression mode: $compressionMode (must be \"none\", \"fast\", or \"best\")")
)
}
} ?: true
}
return PluginConfigValidationResult.VALID
}
@Extension
@@ -1,4 +1,4 @@
Plugin-Version: 1.0.0-alpha1
Plugin-Version: 1.0.0-alpha2
Plugin-Class: de.grimsi.gameyfin.plugins.directdownload.DirectDownloadPlugin
Plugin-Id: de.grimsi.gameyfin.directdownload
Plugin-Name: Direct Download
@@ -5,10 +5,7 @@ import com.api.igdb.exceptions.RequestException
import com.api.igdb.request.IGDBWrapper
import com.api.igdb.request.TwitchAuthenticator
import com.api.igdb.request.games
import de.grimsi.gameyfin.pluginapi.core.Configurable
import de.grimsi.gameyfin.pluginapi.core.GameyfinPlugin
import de.grimsi.gameyfin.pluginapi.core.PluginConfigElement
import de.grimsi.gameyfin.pluginapi.core.PluginConfigError
import de.grimsi.gameyfin.pluginapi.core.*
import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadata
import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadataProvider
import me.xdrop.fuzzywuzzy.FuzzySearch
@@ -36,19 +33,24 @@ class IgdbPlugin(wrapper: PluginWrapper) : GameyfinPlugin(wrapper), Configurable
)
override var config: Map<String, String?> = emptyMap()
override fun validateConfig(config: Map<String, String?>): Boolean {
override fun validateConfig(config: Map<String, String?>): PluginConfigValidationResult {
try {
authenticate()
return true
authenticate(config["clientId"], config["clientSecret"])
return PluginConfigValidationResult.VALID
} catch (e: PluginConfigError) {
log.error(e.message)
return false
return PluginConfigValidationResult.INVALID(
mapOf(
"clientId" to "Invalid client ID and/or client secret",
"clientSecret" to "Invalid client ID and/or client secret"
)
)
}
}
override fun start() {
try {
authenticate()
authenticate(config["clientId"], config["clientSecret"])
} catch (e: PluginConfigError) {
log.error(e.message)
}
@@ -58,11 +60,11 @@ class IgdbPlugin(wrapper: PluginWrapper) : GameyfinPlugin(wrapper), Configurable
log.debug("IgdbPlugin.stop()")
}
private fun authenticate() {
private fun authenticate(clientId: String? = null, clientSecret: String? = null) {
log.debug("Authenticating on Twitch API...")
val clientId: String = config["clientId"] ?: throw PluginConfigError("Twitch Client ID not set")
val clientSecret: String = config["clientSecret"] ?: throw PluginConfigError("Twitch Client Secret not set")
val clientId: String = clientId ?: throw PluginConfigError("Twitch Client ID not set")
val clientSecret: String = clientSecret ?: throw PluginConfigError("Twitch Client Secret not set")
val token = TwitchAuthenticator.requestTwitchToken(clientId, clientSecret)
?: throw PluginConfigError("Failed to authenticate on Twitch API with provided credentials")
+1 -1
View File
@@ -1,4 +1,4 @@
Plugin-Version: 1.0.0-alpha6
Plugin-Version: 1.0.0-alpha7
Plugin-Class: de.grimsi.gameyfin.plugins.igdb.IgdbPlugin
Plugin-Id: de.grimsi.gameyfin.igdb
Plugin-Name: IGDB Metadata
@@ -3,6 +3,7 @@ package de.grimsi.gameyfin.plugins.steamgriddb
import de.grimsi.gameyfin.pluginapi.core.ConfigurableGameyfinPlugin
import de.grimsi.gameyfin.pluginapi.core.PluginConfigElement
import de.grimsi.gameyfin.pluginapi.core.PluginConfigError
import de.grimsi.gameyfin.pluginapi.core.PluginConfigValidationResult
import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadata
import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadataProvider
import de.grimsi.gameyfin.plugins.steamgriddb.api.SteamGridDbApiClient
@@ -28,28 +29,30 @@ class SteamGridDbPlugin(wrapper: PluginWrapper) : ConfigurableGameyfinPlugin(wra
)
)
override fun validateConfig(config: Map<String, String?>): Boolean {
override fun validateConfig(config: Map<String, String?>): PluginConfigValidationResult {
try {
runBlocking { authenticate() }
return true
runBlocking { authenticate(config["apiKey"]) }
return PluginConfigValidationResult.VALID
} catch (e: PluginConfigError) {
log.error(e.message)
return false
return PluginConfigValidationResult.INVALID(
mapOf("apiKey" to "Invalid API key")
)
}
}
override fun start() {
try {
runBlocking { authenticate() }
runBlocking { authenticate(config["apiKey"]) }
} catch (e: PluginConfigError) {
log.error(e.message)
}
}
private suspend fun authenticate() {
private suspend fun authenticate(apiKey: String? = null) {
log.debug("Authenticating on SteamGridDB API...")
val apiKey: String = config["apiKey"] ?: throw PluginConfigError("SteamGridDB API key not set")
val apiKey: String = apiKey ?: throw PluginConfigError("SteamGridDB API key not set")
val client = SteamGridDbApiClient(apiKey)
if (!client.isApiKeyValid()) {
@@ -1,4 +1,4 @@
Plugin-Version: 1.0.0-alpha3
Plugin-Version: 1.0.0-alpha4
Plugin-Class: de.grimsi.gameyfin.plugins.steamgriddb.SteamGridDbPlugin
Plugin-Id: de.grimsi.gameyfin.steamgriddb
Plugin-Name: SteamGridDB Covers