Fix, suppress or modify all Detekt linter issues (#231)

This commit is contained in:
Niels van Velzen 2021-04-18 17:47:03 +02:00 committed by GitHub
parent ae2b202865
commit 55ea8811c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 149 additions and 49 deletions

View File

@ -3,8 +3,9 @@ root = true
[*]
insert_final_newline = true
end_of_line = lf
max_line_length = 120
[{*.kts, *.kt}]
[{*.kts,*.kt}]
charset = utf-8
indent_style = tab
tab_width = 4

View File

@ -1,2 +1,21 @@
build:
maxIssues: 0
complexity:
NestedBlockDepth:
threshold: 6
TooManyFunctions:
thresholdInFiles: 15
thresholdInClasses: 15
thresholdInInterfaces: 15
thresholdInObjects: 15
thresholdInEnums: 15
ignoreDeprecated: true
style:
WildcardImport:
active: false
LoopWithTooManyJumpStatements:
maxJumpCount: 6
ReturnCount:
max: 6

View File

@ -39,7 +39,8 @@ public open class KtorClient(
}
install(HttpTimeout) {
connectTimeoutMillis = 10000
@Suppress("MagicNumber")
connectTimeoutMillis = 10_000 // 10 seconds
}
}
@ -131,12 +132,24 @@ public open class KtorClient(
prefix = "MediaBrowser ",
transform = {
// Check for bad strings to prevent endless hours debugging why the server throws http 500 errors
require(!it.key.contains('=')) { "Key ${it.key} can not contain the = character in the authorization header" }
require(!it.key.contains(',')) { "Key ${it.key} can not contain the , character in the authorization header" }
require(!it.key.startsWith('"') && !it.key.endsWith('"')) { "Key ${it.key} can not start or end with the \" character in the authorization header" }
require(!it.value.contains('=')) { "Value ${it.value} (for key ${it.key}) can not contain the = character in the authorization header" }
require(!it.value.contains(',')) { "Value ${it.value} (for key ${it.key}) can not contain the , character in the authorization header" }
require(!it.value.startsWith('"') && !it.value.endsWith('"')) { "Value ${it.value} (for key ${it.key}) can not start or end with the \" character in the authorization header" }
require(!it.key.contains('=')) {
"Key ${it.key} can not contain the = character in the authorization header"
}
require(!it.key.contains(',')) {
"Key ${it.key} can not contain the , character in the authorization header"
}
require(!it.key.startsWith('"') && !it.key.endsWith('"')) {
"Key ${it.key} can not start or end with the \" character in the authorization header"
}
require(!it.value.contains('=')) {
"Value ${it.value} (for key ${it.key}) can not contain the = character in the authorization header"
}
require(!it.value.contains(',')) {
"Value ${it.value} (for key ${it.key}) can not contain the , character in the authorization header"
}
require(!it.value.startsWith('"') && !it.value.endsWith('"')) {
"Value ${it.value} (for key ${it.key}) can not start or end with the \" character in the authorization header"
}
// key="value"
"""${it.key.capitalize()}="${it.value}""""

View File

@ -41,6 +41,7 @@ public class WebSocketApi(
private const val RECONNECT_DELAY = 3 * 1000L // milliseconds
}
@Suppress("ComplexMethod")
private fun SessionMessageType.getSerializer() = when (this) {
FORCE_KEEP_ALIVE -> serializer<ForceKeepAliveMessage>()
GENERAL_COMMAND -> serializer<GeneralCommandMessage>()
@ -86,7 +87,8 @@ public class WebSocketApi(
private val client: HttpClient = HttpClient {
install(HttpTimeout) {
connectTimeoutMillis = 10000
@Suppress("MagicNumber")
connectTimeoutMillis = 10_000 // 10 seconds
}
install(WebSockets)
@ -226,7 +228,8 @@ public class WebSocketApi(
/**
* Publish a message to the server.
*/
public suspend inline fun <reified T : OutgoingSocketMessage> publish(message: T): Unit = publish(message, serializer())
public suspend inline fun <reified T : OutgoingSocketMessage> publish(message: T): Unit =
publish(message, serializer())
/**
* Publish a message to the server.

View File

@ -8,10 +8,11 @@ import org.jellyfin.sdk.model.DeviceInfo
public data class JellyfinOptions(
val discoverBroadcastAddressesProvider: DiscoveryBroadcastAddressesProvider,
val clientInfo: ClientInfo?,
val deviceInfo: DeviceInfo?
val deviceInfo: DeviceInfo?,
) {
public class Builder {
public var discoveryBroadcastAddressesProvider: DiscoveryBroadcastAddressesProvider = JavaNetBroadcastAddressesProvider()
public var discoveryBroadcastAddressesProvider: DiscoveryBroadcastAddressesProvider =
JavaNetBroadcastAddressesProvider()
public var clientInfo: ClientInfo? = null
public var deviceInfo: DeviceInfo? = null

View File

@ -118,6 +118,7 @@ public class AddressCandidateHelper(
// addBaseUrlCandidates()
}
@Suppress("MagicNumber")
private fun Url.score(): Int {
// Start out with a score of 0
var score = 0

View File

@ -21,7 +21,8 @@ import java.net.SocketTimeoutException
* the maximum amount of servers has been retrieved.
*/
public class LocalServerDiscovery(
private val discoveryBroadcastAddressesProvider: DiscoveryBroadcastAddressesProvider = JavaNetBroadcastAddressesProvider()
private val discoveryBroadcastAddressesProvider: DiscoveryBroadcastAddressesProvider =
JavaNetBroadcastAddressesProvider(),
) {
public companion object {
public const val DISCOVERY_MESSAGE: String = "who is JellyfinServer?"
@ -58,6 +59,7 @@ public class LocalServerDiscovery(
val buffer = ByteArray(DISCOVERY_RECEIVE_BUFFER) // Buffer to receive message in
val packet = DatagramPacket(buffer, buffer.size)
@Suppress("SwallowedException")
return try {
socket.receive(packet)
@ -70,6 +72,7 @@ public class LocalServerDiscovery(
info
} catch (err: SocketTimeoutException) {
// Unable to receive due too timeout, which is common for non-Jellyfin devices
// Just ignore
null
@ -89,7 +92,7 @@ public class LocalServerDiscovery(
*/
public fun discover(
timeout: Int = DISCOVERY_TIMEOUT,
maxServers: Int = DISCOVERY_MAX_SERVERS
maxServers: Int = DISCOVERY_MAX_SERVERS,
): Flow<ServerDiscoveryInfo> = flow {
logger.info("Starting discovery with timeout of ${timeout}ms")
@ -108,6 +111,8 @@ public class LocalServerDiscovery(
// Try reading incoming messages but with a maximum
val foundServers = mutableSetOf<String>()
@Suppress("UnusedPrivateMember")
for (i in 0..maxServers) {
if (socket.isClosed || !GlobalScope.isActive) break

View File

@ -32,7 +32,7 @@ public class RecommendedServerDiscovery(
val info = try {
api.getPublicSystemInfo()
} catch (err: ConnectException) {
logger.debug("Could not connect to $address")
logger.debug("Could not connect to $address", err)
null
} catch (err: Exception) {
logger.error("Could not retrieve public system info for $address", err)
@ -40,6 +40,7 @@ public class RecommendedServerDiscovery(
}
val endTime = System.currentTimeMillis()
@Suppress("MagicNumber")
return SystemInfoResult(
address = address,
systemInfo = if (info != null && info.status == 200) info.content else null,
@ -47,6 +48,7 @@ public class RecommendedServerDiscovery(
)
}
@Suppress("MagicNumber")
private fun assignScore(result: SystemInfoResult, parentResult: SystemInfoResult? = null): RecommendedServerInfo {
var points = 0

View File

@ -33,12 +33,14 @@ public data class ServerVersion(
val parts = str.split('.').map(String::toIntOrNull)
// Check if we found enough parts
@Suppress("MagicNumber")
if (parts.size != 3 && parts.size != 4) return null
// Bad value found
if (parts.any { it == null }) return null
// Return server version
@Suppress("MagicNumber")
return ServerVersion(
major = parts[0]!!,
minor = parts[1]!!,

View File

@ -20,6 +20,7 @@ public fun String.toUUID(): UUID = UUID.fromString(replace(UUID_REGEX, "$1-$2-$3
* Convert string to UUID or null if the string is not an UUID.
* Accepts simple and hyphenated notations.
*/
@Suppress("SwallowedException")
public fun String.toUUIDOrNull(): UUID? = try {
toUUID()
} catch (err: IllegalArgumentException) {

View File

@ -17,9 +17,10 @@ public class AndroidBroadcastAddressesProvider(
* Required the ACCESS_WIFI_STATE permission which is not enabled by default.
*/
@RequiresPermission("android.permission.ACCESS_WIFI_STATE")
@Suppress("MagicNumber")
override suspend fun getBroadcastAddresses(): Collection<InetAddress> {
val wifi = context.getSystemService<WifiManager>() ?: return emptyList()
val dhcp = wifi.dhcpInfo ?: return emptyList()
val wifi = context.getSystemService<WifiManager>()
val dhcp = wifi?.dhcpInfo ?: return emptyList()
val broadcast = dhcp.ipAddress and dhcp.netmask or dhcp.netmask.inv()
val quads = ByteArray(4)
for (k in 0..3) quads[k] = (broadcast shr k * 8).toByte()

View File

@ -5,7 +5,7 @@ import kotlin.reflect.KProperty
typealias Arguments = Map<String, String>
operator fun Arguments.getValue(thisRef: Any?, property: KProperty<*>): String =
this[property.name] ?: throw Error("Missing argument --${property.name}")
this[property.name] ?: throw OpenApiGeneratorError("Missing argument --${property.name}")
fun Array<out String>.asArguments(): Arguments {
val iterator = iterator()

View File

@ -0,0 +1,3 @@
package org.jellyfin.openapi
open class OpenApiGeneratorError(message: String) : Error(message)

View File

@ -11,7 +11,7 @@ import org.jellyfin.openapi.model.ApiServiceOperation
import org.jellyfin.openapi.model.ApiServiceOperationParameter
open class OperationBuilder(
private val deprecatedAnnotationSpecBuilder: DeprecatedAnnotationSpecBuilder
private val deprecatedAnnotationSpecBuilder: DeprecatedAnnotationSpecBuilder,
) : Builder<ApiServiceOperation, FunSpec> {
protected open fun buildFunctionShell(data: ApiServiceOperation) = FunSpec.builder(data.name).apply {
// Make function suspended
@ -53,7 +53,10 @@ open class OperationBuilder(
}.build()
protected fun FunSpec.Builder.addParameterMapStatements(name: String, parameters: Collection<ApiServiceOperationParameter>) {
protected fun FunSpec.Builder.addParameterMapStatements(
name: String,
parameters: Collection<ApiServiceOperationParameter>,
) {
// Create map
// val $(name) = $("emptyMap"|"mutableMapOf")<String, Any?>()
val mapType = if (parameters.isEmpty()) "emptyMap" else "mutableMapOf"

View File

@ -7,9 +7,11 @@ import org.jellyfin.openapi.constants.Strings
import org.jellyfin.openapi.model.ApiServiceOperation
class OperationUrlBuilder(
private val deprecatedAnnotationSpecBuilder: DeprecatedAnnotationSpecBuilder
private val deprecatedAnnotationSpecBuilder: DeprecatedAnnotationSpecBuilder,
) : OperationBuilder(deprecatedAnnotationSpecBuilder) {
override fun buildFunctionShell(data: ApiServiceOperation) = FunSpec.builder(data.name + Strings.URL_OPERATION_SUFFIX).apply {
override fun buildFunctionShell(data: ApiServiceOperation) = FunSpec.builder(
data.name + Strings.URL_OPERATION_SUFFIX
).apply {
// Add description
data.description?.let { addKdoc("%L", it) }

View File

@ -16,15 +16,18 @@ import org.jellyfin.openapi.model.EnumApiModel
import org.jellyfin.openapi.model.JellyFile
class EnumModelBuilder(
private val deprecatedAnnotationSpecBuilder: DeprecatedAnnotationSpecBuilder
private val deprecatedAnnotationSpecBuilder: DeprecatedAnnotationSpecBuilder,
) : Builder<EnumApiModel, JellyFile> {
override fun build(data: EnumApiModel): JellyFile {
return TypeSpec.enumBuilder(data.name.toPascalCase(from = CaseFormat.CAPITALIZED_CAMEL))
.apply {
data.constants.forEach {
addEnumConstant(it.toScreamingSnakeCase(from = CaseFormat.CAPITALIZED_CAMEL), TypeSpec.anonymousClassBuilder().apply {
addAnnotation(AnnotationSpec.builder(SerialName::class).addMember("%S", it).build())
}.build())
addEnumConstant(
it.toScreamingSnakeCase(from = CaseFormat.CAPITALIZED_CAMEL),
TypeSpec.anonymousClassBuilder().apply {
addAnnotation(AnnotationSpec.builder(SerialName::class).addMember("%S", it).build())
}.build()
)
}
data.description?.let { addKdoc(it) }
if (data.deprecated) addAnnotation(deprecatedAnnotationSpecBuilder.build(Strings.DEPRECATED_CLASS))

View File

@ -1,5 +1,6 @@
package org.jellyfin.openapi.builder.model
import org.jellyfin.openapi.OpenApiGeneratorError
import org.jellyfin.openapi.builder.Builder
import org.jellyfin.openapi.model.*
@ -12,6 +13,6 @@ class ModelBuilder(
is EmptyApiModel -> emptyModelBuilder.build(data)
is EnumApiModel -> enumModelBuilder.build(data)
is ObjectApiModel -> objectModelBuilder.build(data)
else -> throw Error("Unknown model class ${data::class.qualifiedName}")
else -> throw OpenApiGeneratorError("Unknown model class ${data::class.qualifiedName}")
}
}

View File

@ -18,6 +18,7 @@ class ObjectModelBuilder(
private val deprecatedAnnotationSpecBuilder: DeprecatedAnnotationSpecBuilder,
private val typeSerializerBuilder: TypeSerializerBuilder
) : Builder<ObjectApiModel, JellyFile> {
@Suppress("ComplexMethod")
override fun build(data: ObjectApiModel): JellyFile {
val properties = mutableListOf<PropertySpec>()
val serializers = mutableSetOf<TypeName>()

View File

@ -6,6 +6,7 @@ import io.swagger.v3.oas.models.PathItem
import io.swagger.v3.oas.models.Paths
import net.pearx.kasechange.CaseFormat
import net.pearx.kasechange.toCamelCase
import org.jellyfin.openapi.OpenApiGeneratorError
import org.jellyfin.openapi.builder.Builder
import org.jellyfin.openapi.builder.api.ApiNameBuilder
import org.jellyfin.openapi.constants.MimeType
@ -41,7 +42,12 @@ class OpenApiApiServicesBuilder(
hook.mapServiceNames(operation, serviceNames)
}
private fun buildOperation(operation: Operation, path: String, serviceName: String, method: HttpMethod): ApiServiceOperation {
private fun buildOperation(
operation: Operation,
path: String,
serviceName: String,
method: HttpMethod,
): ApiServiceOperation {
val operationName = operation.operationId.toCamelCase(from = CaseFormat.CAPITALIZED_CAMEL)
val pathParameters = mutableListOf<ApiServiceOperationParameter>()
@ -54,7 +60,7 @@ class OpenApiApiServicesBuilder(
when (parameterSpec.`in`) {
"path" -> pathParameters
"query" -> queryParameters
else -> throw Error("""Unknown "in": ${parameterSpec.`in`}""")
else -> throw OpenApiGeneratorError("""Unknown "in": ${parameterSpec.`in`}""")
} += ApiServiceOperationParameter(
name = parameterName,
originalName = parameterSpec.name,

View File

@ -10,28 +10,42 @@ import org.jellyfin.openapi.model.*
class OpenApiModelBuilder(
private val openApiTypeBuilder: OpenApiTypeBuilder,
private val modelBuilder: ModelBuilder
private val modelBuilder: ModelBuilder,
) : Builder<Schema<Any>, JellyFile> {
override fun build(data: Schema<Any>): JellyFile {
val model = when {
// Object
data.type == "object" -> when (data.properties.isNullOrEmpty()) {
// No properties use the empty model
true -> EmptyApiModel(data.name, data.description, data.deprecated == true)
true -> EmptyApiModel(
data.name,
data.description,
data.deprecated == true
)
// Otherwise use the object model
false -> ObjectApiModel(data.name, data.description, data.deprecated == true, data.properties.map { (originalName, property) ->
val name = originalName.toCamelCase(from = CaseFormat.CAPITALIZED_CAMEL)
ObjectApiModelProperty(
name = name,
originalName = originalName,
type = openApiTypeBuilder.build(ModelTypePath(data.name, name), property),
description = property.description,
deprecated = property.deprecated == true
)
}.toSet())
false -> ObjectApiModel(
data.name,
data.description,
data.deprecated == true,
data.properties.map { (originalName, property) ->
val name = originalName.toCamelCase(from = CaseFormat.CAPITALIZED_CAMEL)
ObjectApiModelProperty(
name = name,
originalName = originalName,
type = openApiTypeBuilder.build(ModelTypePath(data.name, name), property),
description = property.description,
deprecated = property.deprecated == true
)
}.toSet()
)
}
// Enum
data.enum.isNotEmpty() -> EnumApiModel(data.name, data.description, data.deprecated == true, data.enum.orEmpty().map { it.toString() }.toSet())
data.enum.isNotEmpty() -> EnumApiModel(
data.name,
data.description,
data.deprecated == true,
data.enum.orEmpty().map { it.toString() }.toSet()
)
// Unknown type
else -> throw NotImplementedError("Unknown top-level type: ${data.type} for ${data.name}")

View File

@ -10,6 +10,7 @@ import org.jellyfin.openapi.hooks.ApiTypePath
class OpenApiReturnTypeBuilder(
private val openApiTypeBuilder: OpenApiTypeBuilder
) {
@Suppress("ComplexMethod")
fun build(path: ApiTypePath, response: ApiResponse?): TypeName {
val supportedReturnMimeTypes = response?.content?.keys.orEmpty()

View File

@ -7,6 +7,7 @@ import com.squareup.kotlinpoet.asTypeName
import io.swagger.v3.oas.models.media.*
import net.pearx.kasechange.CaseFormat
import net.pearx.kasechange.toPascalCase
import org.jellyfin.openapi.OpenApiGeneratorError
import org.jellyfin.openapi.builder.openapi.OpenApiReturnTypeBuilder.Companion.TYPE_BINARY
import org.jellyfin.openapi.builder.openapi.OpenApiReturnTypeBuilder.Companion.TYPE_STRING
import org.jellyfin.openapi.constants.Packages
@ -16,7 +17,7 @@ import java.time.LocalDateTime
import java.util.*
class OpenApiTypeBuilder(
private val hooks: Collection<TypeBuilderHook>
private val hooks: Collection<TypeBuilderHook>,
) {
fun build(path: TypePath, schema: Schema<*>): TypeName =
buildWithHooks(path, schema) ?: buildSchema(schema)
@ -30,6 +31,7 @@ class OpenApiTypeBuilder(
return null
}
@Suppress("ComplexMethod")
fun buildSchema(schema: Schema<*>): TypeName = when {
// Use referenced type
schema.`$ref` != null -> buildReference(schema.`$ref`)
@ -95,5 +97,8 @@ class OpenApiTypeBuilder(
fun buildBinary() = TYPE_BINARY
class UnknownTypeError(type: String?, format: String?) : Error("Unknown type $type with format $format")
class UnknownTypeError(
type: String?,
format: String?,
) : OpenApiGeneratorError("Unknown type $type with format $format")
}

View File

@ -4,5 +4,14 @@ import com.github.ajalt.clikt.core.ParameterHolder
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.options.required
fun ParameterHolder.serverOption() = option("-s", "--server", help = "Url of the server", envvar = "JELLYFIN_SERVER").required()
fun ParameterHolder.tokenOption() = option("-t", "--token", help = "Access token", envvar = "JELLYFIN_TOKEN").required()
fun ParameterHolder.serverOption() = option(
"-s", "--server",
help = "Url of the server",
envvar = "JELLYFIN_SERVER"
).required()
fun ParameterHolder.tokenOption() = option(
"-t", "--token",
help = "Access token",
envvar = "JELLYFIN_TOKEN"
).required()

View File

@ -6,15 +6,18 @@ import com.github.ajalt.clikt.parameters.options.option
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking
import org.jellyfin.sample.cli.serverOption
import org.jellyfin.sdk.Jellyfin
import org.jellyfin.sdk.api.operations.SystemApi
import org.jellyfin.sample.cli.serverOption
class Ping(
private val jellyfin: Jellyfin
private val jellyfin: Jellyfin,
) : CliktCommand("Pings a given server and retrieve basic system information") {
private val server by serverOption()
private val extended by option("-e", "--extended", help = "Find servers based on input using recommended server algorithm").flag(default = false)
private val extended by option(
"-e", "--extended",
help = "Find servers based on input using recommended server algorithm"
).flag(default = false)
override fun run() = runBlocking {
if (extended) runExtended()