Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import io.ktor.client.request.header
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMessageBuilder
import io.ktor.http.isSuccess
import io.ktor.serialization.ContentConvertException
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import kotlinx.io.IOException
Expand Down Expand Up @@ -68,10 +69,13 @@ internal class CLISelfUpdateResolver(
?.body<GithubRelease>()
?.tagName
} catch (ignored: IOException) {
logger.error("Failed to fetch latest release from $UPDATE_URL", ignored)
logger.error(ERROR_MESSAGE, ignored)
null
} catch (ignored: SendCountExceedException) {
logger.error("Failed to fetch latest release from $UPDATE_URL", ignored)
logger.error(ERROR_MESSAGE, ignored)
null
} catch (ignored: ContentConvertException) {
logger.error(ERROR_MESSAGE, ignored)
null
}
} ?: return null
Expand All @@ -98,6 +102,7 @@ internal class CLISelfUpdateResolver(
companion object {
private val TAG_REGEX = Regex("v(.*)")
const val UPDATE_URL = "https://api.github.com/repos/deezer/caupain/releases/latest"
val ERROR_MESSAGE = "Failed to fetch latest release from $UPDATE_URL"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ import com.deezer.caupain.resolver.SelfUpdateResolver
import com.deezer.caupain.resolver.UpdatedVersionResolver
import com.deezer.caupain.serialization.DefaultJson
import com.deezer.caupain.serialization.DefaultToml
import com.deezer.caupain.serialization.DefaultXml
import com.deezer.caupain.serialization.xml.DefaultXml
import dev.drewhamilton.poko.Poko
import io.ktor.client.HttpClient
import io.ktor.client.plugins.HttpRequestRetry
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import io.ktor.client.engine.HttpClientEngineConfig
import io.ktor.client.plugins.SendCountExceedException
import io.ktor.client.statement.HttpResponse
import io.ktor.http.isSuccess
import io.ktor.serialization.ContentConvertException
import kotlinx.io.IOException

internal expect fun HttpClientEngineConfig.configureKtorEngine()
Expand All @@ -54,6 +55,9 @@ internal suspend inline fun <reified T, R> HttpClient.processRequest(
} catch (ignored: SendCountExceedException) {
onRecoverableError(ignored)
default
} catch (ignored: ContentConvertException) {
onRecoverableError(ignored)
default
}
}

Expand All @@ -70,4 +74,4 @@ internal suspend inline fun <reified R> HttpClient.processRequest(
onRecoverableError = onRecoverableError,
executeRequest = executeRequest,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,16 @@

package com.deezer.caupain.model.maven

import com.deezer.caupain.serialization.xml.MavenInfoSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import nl.adaptivity.xmlutil.serialization.XmlChildrenName
import nl.adaptivity.xmlutil.serialization.XmlElement

@Serializable
@SerialName("project")
@Serializable(MavenInfoSerializer::class)
internal data class MavenInfo(
@XmlElement(true) val name: String? = null,
@XmlElement(true) val url: String? = null,
@XmlChildrenName("dependency") val dependencies: List<Dependency> = emptyList(),
val name: String? = null,
val url: String? = null,
val dependencies: List<Dependency> = emptyList(),
val scm: SCMInfos? = null,
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
@file:Suppress("UnusedImport") // Detekt bug

package com.deezer.caupain.serialization.xml

import nl.adaptivity.xmlutil.XmlDelegatingReader
import nl.adaptivity.xmlutil.XmlReader
import nl.adaptivity.xmlutil.localPart
import nl.adaptivity.xmlutil.namespaceURI
import nl.adaptivity.xmlutil.prefix
import nl.adaptivity.xmlutil.serialization.structure.XmlDescriptor

internal class DynamicTagReader(
private val idPropertyName: String,
reader: XmlReader,
descriptor: XmlDescriptor
) : XmlDelegatingReader(reader) {

private val filterDepth = delegate.depth - reader.depth

private val elementName = descriptor.tagName

private val idAttrName = (0 until descriptor.elementsCount)
.first { descriptor.serialDescriptor.getElementName(it) == idPropertyName }
.let { descriptor.getElementDescriptor(it) }
.tagName

private val idValue = delegate.localName

override val attributeCount: Int
get() = if (filterDepth == 0) super.attributeCount + 1 else super.attributeCount

override fun getAttributeNamespace(index: Int): String = if (filterDepth == 0) {
if (index == 0) idAttrName.namespaceURI else super.getAttributeNamespace(index - 1)
} else {
super.getAttributeNamespace(index)
}

override fun getAttributePrefix(index: Int): String = if (filterDepth == 0) {
if (index == 0) idAttrName.prefix else super.getAttributePrefix(index - 1)
} else {
super.getAttributePrefix(index)
}

override fun getAttributeLocalName(index: Int): String = if (filterDepth == 0) {
if (index == 0) idAttrName.localPart else super.getAttributeLocalName(index - 1)
} else {
super.getAttributeLocalName(index)
}

override fun getAttributeValue(index: Int): String = if (filterDepth == 0) {
if (index == 0) idValue else super.getAttributeValue(index - 1)
} else {
super.getAttributeValue(index)
}

@Suppress("UnnecessaryParentheses") // More understandable this way
override fun getAttributeValue(nsUri: String?, localName: String): String? =
if (
filterDepth == 0 &&
nsUri.orEmpty() == idAttrName.namespaceURI &&
localName == idAttrName.localPart
) {
idValue
} else {
super.getAttributeValue(nsUri, localName)
}

override val namespaceURI: String
get() = if (filterDepth == 0) elementName.namespaceURI else super.namespaceURI

override val localName: String
get() = if (filterDepth == 0) elementName.localPart else super.localName

override val prefix: String
get() = if (filterDepth == 0) elementName.prefix else super.prefix
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.deezer.caupain.serialization.xml

import com.deezer.caupain.model.maven.Dependency
import com.deezer.caupain.model.maven.MavenInfo
import com.deezer.caupain.model.maven.SCMInfos
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.serializer
import nl.adaptivity.xmlutil.serialization.XmlChildrenName
import nl.adaptivity.xmlutil.serialization.XmlElement
import nl.adaptivity.xmlutil.serialization.XmlSerialName

@Serializable
@SerialName("project")
private data class RawMavenInfo(
@XmlElement val name: String? = null,
@XmlElement val url: String? = null,
@XmlChildrenName("dependency") val dependencies: List<Dependency> = emptyList(),
@XmlElement @XmlSerialName("scm") val scm: SCMInfos? = null,
@XmlElement
@XmlSerialName("properties")
@Serializable(PropertiesMapSerializer::class)
val properties: Map<String, String> = emptyMap(),
) {
constructor(mavenInfo: MavenInfo) : this(
name = mavenInfo.name,
url = mavenInfo.url,
dependencies = mavenInfo.dependencies,
scm = mavenInfo.scm,
)

fun resolved(): MavenInfo {
return if (properties.isEmpty()) {
return MavenInfo(
name = name,
url = url,
dependencies = dependencies,
scm = scm,
)
} else {
MavenInfo(
name = name?.resolve(properties),
url = url?.resolve(properties),
dependencies = dependencies.map { dependency ->
Dependency(
groupId = dependency.groupId.resolve(properties),
artifactId = dependency.artifactId.resolve(properties),
version = dependency.version?.resolve(properties),
)
},
scm = scm?.let { scmInfos ->
SCMInfos(
url = scmInfos.url?.resolve(properties)
)
}
)
}
}

companion object {
private fun String.resolve(properties: Map<String, String>): String {
return PLACEHOLDER_REGEX.replace(this) { matchResult ->
properties[matchResult.groupValues[1]] ?: matchResult.value
}
}

private val PLACEHOLDER_REGEX = "\\$\\{(.+?)\\}".toRegex()
}
}

internal object MavenInfoSerializer : KSerializer<MavenInfo> {

private val rawSerializer = serializer<RawMavenInfo>()

override val descriptor: SerialDescriptor
get() = rawSerializer.descriptor

override fun serialize(
encoder: Encoder,
value: MavenInfo
) {
encoder.encodeSerializableValue(rawSerializer, RawMavenInfo(value))
}

override fun deserialize(decoder: Decoder): MavenInfo {
return decoder.decodeSerializableValue(rawSerializer).resolved()
}
}
Loading