diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3bb3347df..bc3b53c6f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -45,4 +45,4 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build and test with Gradle - run: ./gradlew clean licensee :ktorfit-annotations:publishToMavenLocal :ktorfit-ksp:test :ktorfit-lib-common:jvmTest + run: ./gradlew licensee :ktorfit-annotations:publishToMavenLocal :ktorfit-ksp:test :ktorfit-lib-common:jvmTest :ktorfit-converters:call:publishToMavenLocal diff --git a/ktorfit-converters/call/build.gradle.kts b/ktorfit-converters/call/build.gradle.kts index f7dd7d11f..23e75e5ca 100644 --- a/ktorfit-converters/call/build.gradle.kts +++ b/ktorfit-converters/call/build.gradle.kts @@ -14,7 +14,7 @@ licensee { } -val enableSigning = true +val enableSigning = project.hasProperty("signingInMemoryKey") mavenPublishing { diff --git a/ktorfit-converters/call/src/commonMain/kotlin/de/jensklingenberg/ktorfit/converter/builtin/CallConverterFactory.kt b/ktorfit-converters/call/src/commonMain/kotlin/de/jensklingenberg/ktorfit/converter/builtin/CallConverterFactory.kt index 811058ef2..96093978c 100644 --- a/ktorfit-converters/call/src/commonMain/kotlin/de/jensklingenberg/ktorfit/converter/builtin/CallConverterFactory.kt +++ b/ktorfit-converters/call/src/commonMain/kotlin/de/jensklingenberg/ktorfit/converter/builtin/CallConverterFactory.kt @@ -31,7 +31,6 @@ public class CallConverterFactory : Converter.Factory { ?: response.body(typeData.typeArgs.first().typeInfo) callBack.onResponse(convertedBody, response) } catch (ex: Exception) { - println(ex) callBack.onError(ex) } } @@ -40,16 +39,6 @@ public class CallConverterFactory : Converter.Factory { } } - override fun responseConverter( - typeData: TypeData, - ktorfit: Ktorfit - ): Converter.ResponseConverter? { - if (typeData.typeInfo.type == Call::class) { - return CallResponseConverter(typeData, ktorfit) - } - return null - } - private class CallSuspendResponseConverter(val typeData: TypeData, val ktorfit: Ktorfit) : Converter.SuspendResponseConverter> { override suspend fun convert(response: HttpResponse): Call { @@ -72,6 +61,17 @@ public class CallConverterFactory : Converter.Factory { } } + override fun responseConverter( + typeData: TypeData, + ktorfit: Ktorfit + ): Converter.ResponseConverter? { + if (typeData.typeInfo.type == Call::class) { + return CallResponseConverter(typeData, ktorfit) + } + return null + } + + override fun suspendResponseConverter( typeData: TypeData, ktorfit: Ktorfit diff --git a/ktorfit-converters/flow/src/commonMain/kotlin/de/jensklingenberg/ktorfit/converter/builtin/FlowConverterFactory.kt b/ktorfit-converters/flow/src/commonMain/kotlin/de/jensklingenberg/ktorfit/converter/builtin/FlowConverterFactory.kt index 011739592..6ddcb37e3 100644 --- a/ktorfit-converters/flow/src/commonMain/kotlin/de/jensklingenberg/ktorfit/converter/builtin/FlowConverterFactory.kt +++ b/ktorfit-converters/flow/src/commonMain/kotlin/de/jensklingenberg/ktorfit/converter/builtin/FlowConverterFactory.kt @@ -25,7 +25,6 @@ public class FlowConverterFactory : Converter.Factory { override fun convert(getResponse: suspend () -> HttpResponse): Flow { val requestType = typeData.typeArgs.first() return flow { - try { val response = getResponse() if (requestType.typeInfo.type == HttpResponse::class) { emit(response) @@ -35,13 +34,8 @@ public class FlowConverterFactory : Converter.Factory { typeData.typeArgs.first() )?.convert(response) ?: response.body(typeData.typeArgs.first().typeInfo) - Response.success(convertedBody, response) - emit(convertedBody) } - } catch (exception: Exception) { - throw exception - } } } } diff --git a/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/model/ClassData.kt b/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/model/ClassData.kt index 07c13a474..a49d0a4b5 100644 --- a/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/model/ClassData.kt +++ b/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/model/ClassData.kt @@ -39,10 +39,8 @@ fun ClassData.getImplClassFileSource(resolver: Resolver): String { .addMember("InternalKtorfitApi::class") .build() - val createExtensionFunctionSpec = getCreateExtensionFunctionSpec(classData) - val properties = classData.properties.map { property -> val propBuilder = PropertySpec.builder( property.simpleName.asString(), @@ -108,7 +106,6 @@ fun ClassData.getImplClassFileSource(resolver: Resolver): String { private fun getCreateExtensionFunctionSpec( classData: ClassData ): FunSpec { - return FunSpec.builder("create${classData.name}") .addModifiers(classData.modifiers) .addStatement("return this.create(_${classData.name}Impl())") @@ -125,11 +122,14 @@ private fun getCreateExtensionFunctionSpec( */ fun KSClassDeclaration.toClassData(logger: KSPLogger): ClassData { val ksClassDeclaration = this - val imports = ksClassDeclaration.getFileImports().toMutableList().also { - it.add("io.ktor.util.reflect.*") - it.add("io.ktor.client.request.*") - it.add("io.ktor.http.*") + val imports = ksClassDeclaration.getFileImports().toMutableList().apply { + add("io.ktor.util.reflect.*") + add("io.ktor.client.request.*") + add("io.ktor.http.*") + add(ktorfitClass.packageName + "." + ktorfitClass.name) + add("de.jensklingenberg.ktorfit.internal.*") } + val packageName = ksClassDeclaration.packageName.asString() val className = ksClassDeclaration.simpleName.asString() @@ -140,8 +140,10 @@ fun KSClassDeclaration.toClassData(logger: KSPLogger): ClassData { return@map funcDeclaration.toFunctionData(logger) } - if (functionDataList.any { it -> it.annotations.any { it is FormUrlEncoded || it is Multipart } || - it.parameterDataList.any { param -> param.hasAnnotation() || param.hasAnnotation() } }) { + if (functionDataList.any { it -> + it.annotations.any { it is FormUrlEncoded || it is Multipart } || + it.parameterDataList.any { param -> param.hasAnnotation() || param.hasAnnotation() } + }) { imports.add("io.ktor.client.request.forms.*") } @@ -156,7 +158,6 @@ fun KSClassDeclaration.toClassData(logger: KSPLogger): ClassData { } val properties = ksClassDeclaration.getAllProperties().toList() - return ClassData( name = className, packageName = packageName, @@ -165,7 +166,7 @@ fun KSClassDeclaration.toClassData(logger: KSPLogger): ClassData { superClasses = filteredSupertypes, properties = properties, modifiers = ksClassDeclaration.modifiers.mapNotNull { it.toKModifier() }, - ksFile = this.getKsFile() + ksFile = ksClassDeclaration.getKsFile() ) } @@ -173,11 +174,13 @@ private fun checkClassForErrors(ksClassDeclaration: KSClassDeclaration, logger: val isJavaClass = ksClassDeclaration.origin.name == "JAVA" if (isJavaClass) { logger.error(KtorfitError.JAVA_INTERFACES_ARE_NOT_SUPPORTED, ksClassDeclaration) + return } val isInterface = ksClassDeclaration.classKind == ClassKind.INTERFACE if (!isInterface) { logger.error(KtorfitError.API_DECLARATIONS_MUST_BE_INTERFACES, ksClassDeclaration) + return } val hasTypeParameters = ksClassDeclaration.typeParameters.isNotEmpty() @@ -186,10 +189,12 @@ private fun checkClassForErrors(ksClassDeclaration: KSClassDeclaration, logger: KtorfitError.TYPE_PARAMETERS_ARE_UNSUPPORTED_ON + " ${ksClassDeclaration.simpleName.asString()}", ksClassDeclaration ) + return } if (ksClassDeclaration.packageName.asString().isEmpty()) { logger.error(KtorfitError.INTERFACE_NEEDS_TO_HAVE_A_PACKAGE, ksClassDeclaration) + return } } diff --git a/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/model/KtorfitError.kt b/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/model/KtorfitError.kt index ba330a71a..3ac0f4a92 100644 --- a/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/model/KtorfitError.kt +++ b/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/model/KtorfitError.kt @@ -1,6 +1,6 @@ package de.jensklingenberg.ktorfit.model -class KtorfitError { +internal class KtorfitError { companion object { const val FUNCTION_OR_PARAMETERS_TYPES_MUST_NOT_INCLUDE_ATYPE_VARIABLE_OR_WILDCARD = @@ -12,7 +12,6 @@ class KtorfitError { const val QUERY_MAP_PARAMETER_TYPE_MUST_BE_MAP = "@QueryMap parameter type must be Map." const val QUERY_MAP_KEYS_MUST_BE_OF_TYPE_STRING = "@QueryMap keys must be of type String:" const val URL_PARAMETER_TYPE_MAY_NOT_BE_NULLABLE = "Url parameter type may not be nullable" - const val FIELD_MAP_PARAMETER_TYPE_MUST_BE_MAP = "@FieldMap parameter type must be Map." const val FIELD_MAP_KEYS_MUST_BE_OF_TYPE_STRING = "@FieldMap keys must be of type String:" const val ONLY_ONE_REQUEST_BUILDER_IS_ALLOWED = "Only one RequestBuilder is allowed." @@ -26,22 +25,12 @@ class KtorfitError { const val PART_PARAMETER_TYPE_MAY_NOT_BE_NULLABLE = "Part parameter type may not be nullable" const val PATH_PARAMETER_TYPE_MAY_NOT_BE_NULLABLE = "Path parameter type may not be nullable" const val API_DECLARATIONS_MUST_BE_INTERFACES = "API declarations must be interfaces." - const val PATH_CAN_ONLY_BE_USED_WITH_RELATIVE_URL_ON = "@Path can only be used with relative url on " const val NON_BODY_HTTP_METHOD_CANNOT_CONTAIN_BODY = "Non-body HTTP method cannot contain @Body" const val BODY_PARAMETERS_CANNOT_BE_USED_WITH_FORM_OR_MULTI_PART_ENCODING = "@Body parameters cannot be used with form or multi-part encoding" const val FOR_STREAMING_THE_RETURN_TYPE_MUST_BE_HTTP_STATEMENT = "For streaming the return type must be io.ktor.client.statement.HttpStatement" - - fun MISSING_EITHER_KEYWORD_URL_OrURL_PARAMETER(keyword: String) = - "Missing either @$keyword URL or @Url parameter" - - fun NO_KTORFIT_ANNOTATION_FOUND_AT(parameterName: String): String { - return "No Ktorfit Annotation found at $parameterName" - } - - fun MISSING_X_IN_RELATIVE_URL_PATH(keyword: String) = "Missing {${keyword}} in relative url path" const val JAVA_INTERFACES_ARE_NOT_SUPPORTED = "Java Interfaces are not supported" const val INTERFACE_NEEDS_TO_HAVE_A_PACKAGE = "Interface needs to have a package" const val ONLY_ONE_HTTP_METHOD_IS_ALLOWED = "Only one HTTP method is allowed." @@ -53,11 +42,13 @@ class KtorfitError { const val MULTIPART_CAN_ONLY_BE_SPECIFIED_ON_HTTPMETHODS = "Multipart can only be specified on HTTP methods with request body (e.g., @POST)" const val VARARG_NOT_SUPPORTED_USE_LIST_OR_ARRAY = "vararg not supported use List or Array" - const val NULLABLE_PARAMETERS_ARE_NOT_SUPPORTED = "Nullable Parameters Are Not Supported" - fun NO_HTTP_ANNOTATION_AT(functionName: String) = "No Http annotation at $functionName" - fun URL_CAN_ONLY_BE_USED_WITH_EMPY(keyword: String) = "@Url can only be used with empty @${keyword} URL value" const val HEADERS_VALUE_MUST_BE_IN_FORM = "@Headers value must be in the form \"Name: Value\". Found: " const val PROPERTIES_NOT_SUPPORTED = "throw IllegalStateException(\"Properties not supported by Ktorfit\")" + fun MISSING_EITHER_KEYWORD_URL_OrURL_PARAMETER(keyword: String) = "Missing either @$keyword URL or @Url parameter" + fun NO_KTORFIT_ANNOTATION_FOUND_AT(parameterName: String): String = "No Ktorfit Annotation found at $parameterName" + fun MISSING_X_IN_RELATIVE_URL_PATH(keyword: String) = "Missing {${keyword}} in relative url path" + fun NO_HTTP_ANNOTATION_AT(functionName: String) = "No Http annotation at $functionName" + fun URL_CAN_ONLY_BE_USED_WITH_EMPY(keyword: String) = "@Url can only be used with empty @${keyword} URL value" } } \ No newline at end of file diff --git a/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/model/annotations/FunctionAnnotation.kt b/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/model/annotations/FunctionAnnotation.kt index d45543931..f0111c8ab 100644 --- a/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/model/annotations/FunctionAnnotation.kt +++ b/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/model/annotations/FunctionAnnotation.kt @@ -1,7 +1,13 @@ package de.jensklingenberg.ktorfit.model.annotations enum class HttpMethod(val keyword: String) { - GET("GET"), POST("POST"), PUT("PUT"), DELETE("DELETE"), HEAD("HEAD"), PATCH("PATCH"), CUSTOM("") + GET("GET"), + POST("POST"), + PUT("PUT"), + DELETE("DELETE"), + HEAD("HEAD"), + PATCH("PATCH"), + CUSTOM("") } diff --git a/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/reqBuilderExtension/BodyCodeGenerator.kt b/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/reqBuilderExtension/BodyCodeGenerator.kt index ebc091d5e..85c7810c7 100644 --- a/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/reqBuilderExtension/BodyCodeGenerator.kt +++ b/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/reqBuilderExtension/BodyCodeGenerator.kt @@ -4,7 +4,5 @@ import de.jensklingenberg.ktorfit.model.ParameterData import de.jensklingenberg.ktorfit.model.annotations.ParameterAnnotation.Body fun getBodyDataText(params: List): String { - return params.firstOrNull { it.hasAnnotation() }?.let { - "setBody(%s)".format(it.name) - } ?: "" + return params.firstOrNull { it.hasAnnotation() }?.name?.let { "setBody($it)" } ?: "" } diff --git a/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/utils/Utils.kt b/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/utils/Utils.kt index bcc26f36d..23f2fb31d 100644 --- a/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/utils/Utils.kt +++ b/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/utils/Utils.kt @@ -6,7 +6,6 @@ import com.google.devtools.ksp.symbol.KSType import com.squareup.kotlinpoet.FileSpec import de.jensklingenberg.ktorfit.model.FunctionData import de.jensklingenberg.ktorfit.model.WILDCARDIMPORT -import de.jensklingenberg.ktorfit.model.ktorfitClass import java.io.File fun KSType?.resolveTypeName(): String { @@ -14,12 +13,10 @@ fun KSType?.resolveTypeName(): String { return this.toString().removePrefix("[typealias ").removeSuffix("]") } - inline fun FunctionData.findAnnotationOrNull(): T? { return this.annotations.firstOrNull { it is T } as? T } - fun String.prefixIfNotEmpty(s: String): String { return (s + this).takeIf { this.isNotEmpty() } ?: this } @@ -32,28 +29,17 @@ fun String.surroundIfNotEmpty(prefix: String = "", postFix: String = ""): String return this.prefixIfNotEmpty(prefix).postfixIfNotEmpty(postFix) } - -fun String.surroundWith(s: String): String { - return s + this + s -} - /** * Gets the imports of a class by reading the imports from the file * which contains the class * TODO: Find better way to get imports */ fun KSClassDeclaration.getFileImports(): List { - val importList = - File(this.containingFile!!.filePath) - .readLines() - .filter { it.trimStart().startsWith("import") } - .filter { !it.startsWith("import de.jensklingenberg.ktorfit.http.") } - .toMutableSet() - - importList.add(ktorfitClass.packageName + "." + ktorfitClass.name) - importList.add("de.jensklingenberg.ktorfit.internal.*") - - return importList.map { it.removePrefix("import ") } + return File(this.containingFile!!.filePath) + .readLines() + .filter { it.trimStart().startsWith("import") && !it.startsWith("import de.jensklingenberg.ktorfit.http.") } + .toMutableSet() + .map { it.removePrefix("import ") } } diff --git a/ktorfit-lib-common/api/android/ktorfit-lib-common.api b/ktorfit-lib-common/api/android/ktorfit-lib-common.api index d00bd450a..cefcabc44 100644 --- a/ktorfit-lib-common/api/android/ktorfit-lib-common.api +++ b/ktorfit-lib-common/api/android/ktorfit-lib-common.api @@ -159,14 +159,10 @@ public final class de/jensklingenberg/ktorfit/internal/RequestData { public fun (Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lio/ktor/util/reflect/TypeInfo;)V public synthetic fun (Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lio/ktor/util/reflect/TypeInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Lkotlin/jvm/functions/Function1; - public final fun component2 ()Ljava/lang/String; - public final fun component3 ()Lio/ktor/util/reflect/TypeInfo; public final fun copy (Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lio/ktor/util/reflect/TypeInfo;)Lde/jensklingenberg/ktorfit/internal/RequestData; public static synthetic fun copy$default (Lde/jensklingenberg/ktorfit/internal/RequestData;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lio/ktor/util/reflect/TypeInfo;ILjava/lang/Object;)Lde/jensklingenberg/ktorfit/internal/RequestData; public fun equals (Ljava/lang/Object;)Z public final fun getKtorfitRequestBuilder ()Lkotlin/jvm/functions/Function1; - public final fun getReturnTypeInfo ()Lio/ktor/util/reflect/TypeInfo; - public final fun getReturnTypeName ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } diff --git a/ktorfit-lib-common/api/jvm/ktorfit-lib-common.api b/ktorfit-lib-common/api/jvm/ktorfit-lib-common.api index a69587f4e..d8b236bf9 100644 --- a/ktorfit-lib-common/api/jvm/ktorfit-lib-common.api +++ b/ktorfit-lib-common/api/jvm/ktorfit-lib-common.api @@ -152,14 +152,10 @@ public final class de/jensklingenberg/ktorfit/internal/RequestData { public fun (Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lio/ktor/util/reflect/TypeInfo;)V public synthetic fun (Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lio/ktor/util/reflect/TypeInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Lkotlin/jvm/functions/Function1; - public final fun component2 ()Ljava/lang/String; - public final fun component3 ()Lio/ktor/util/reflect/TypeInfo; public final fun copy (Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lio/ktor/util/reflect/TypeInfo;)Lde/jensklingenberg/ktorfit/internal/RequestData; public static synthetic fun copy$default (Lde/jensklingenberg/ktorfit/internal/RequestData;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lio/ktor/util/reflect/TypeInfo;ILjava/lang/Object;)Lde/jensklingenberg/ktorfit/internal/RequestData; public fun equals (Ljava/lang/Object;)Z public final fun getKtorfitRequestBuilder ()Lkotlin/jvm/functions/Function1; - public final fun getReturnTypeInfo ()Lio/ktor/util/reflect/TypeInfo; - public final fun getReturnTypeName ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } diff --git a/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/Ktorfit.kt b/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/Ktorfit.kt index e37e20b8b..8d1086fe3 100644 --- a/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/Ktorfit.kt +++ b/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/Ktorfit.kt @@ -7,13 +7,10 @@ import de.jensklingenberg.ktorfit.Strings.Companion.EXPECTED_URL_SCHEME import de.jensklingenberg.ktorfit.converter.Converter import de.jensklingenberg.ktorfit.converter.SuspendResponseConverter import de.jensklingenberg.ktorfit.converter.builtin.KtorfitDefaultConverterFactory -import de.jensklingenberg.ktorfit.converter.builtin.DefaultSuspendResponseConverterFactory import de.jensklingenberg.ktorfit.converter.request.CoreResponseConverter import de.jensklingenberg.ktorfit.converter.request.RequestConverter import de.jensklingenberg.ktorfit.converter.request.ResponseConverter import de.jensklingenberg.ktorfit.internal.* -import de.jensklingenberg.ktorfit.internal.DefaultKtorfitService -import de.jensklingenberg.ktorfit.internal.KtorfitClient import io.ktor.client.* import io.ktor.client.engine.* import io.ktor.client.statement.* @@ -26,9 +23,9 @@ import kotlin.reflect.KClass public class Ktorfit private constructor( public val baseUrl: String, public val httpClient: HttpClient = HttpClient(), - public val responseConverters: Set, - public val suspendResponseConverters: Set, - internal val requestConverters: Set, + @Deprecated("Use converterFactories") public val responseConverters: Set, + @Deprecated("Use converterFactories") public val suspendResponseConverters: Set, + @Deprecated("Use converterFactories") internal val requestConverters: Set, private val converterFactories: List ) { @@ -41,13 +38,9 @@ public class Ktorfit private constructor( type: TypeData ): Converter.ResponseConverter? { val start = converterFactories.indexOf(currentFactory) + 1 - (start until converterFactories.size).forEach { - val converter = converterFactories[it].responseConverter(type, this) - if (converter != null) { - return converter - } - } - return null + return converterFactories + .subList(start, converterFactories.size) + .firstNotNullOfOrNull { it.responseConverter(type, this) } } /** @@ -59,13 +52,9 @@ public class Ktorfit private constructor( type: TypeData ): Converter.SuspendResponseConverter? { val start = converterFactories.indexOf(currentFactory) + 1 - (start until converterFactories.size).forEach { - val converter = converterFactories[it].suspendResponseConverter(type, this) - if (converter != null) { - return converter - } - } - return null + return converterFactories + .subList(start, converterFactories.size) + .firstNotNullOfOrNull { it.suspendResponseConverter(type, this) } } /** @@ -78,13 +67,9 @@ public class Ktorfit private constructor( requestType: KClass<*> ): Converter.RequestParameterConverter? { val start = converterFactories.indexOf(currentFactory) + 1 - (start until converterFactories.size).forEach { - val converter = converterFactories[it].requestParameterConverter(parameterType, requestType) - if (converter != null) { - return converter - } - } - return null + return converterFactories + .subList(start, converterFactories.size) + .firstNotNullOfOrNull { it.requestParameterConverter(parameterType, requestType) } } /** @@ -241,11 +226,11 @@ public fun ktorfitBuilder(builder: Ktorfit.Builder.() -> Unit): Ktorfit.Builder @Deprecated("Use the non-Extension function") /** - * This will return an implementation of [T] if [T] is an interface - * with Ktorfit annotations. - * @param ktorfitService Please keep the default parameter, it will be replaced - * by the compiler plugin - */ + * This will return an implementation of [T] if [T] is an interface + * with Ktorfit annotations. + * @param ktorfitService Please keep the default parameter, it will be replaced + * by the compiler plugin + */ public fun Ktorfit.create(ktorfitService: KtorfitService = DefaultKtorfitService()): T { return this.create(ktorfitService) } diff --git a/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/Response.kt b/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/Response.kt index 5bcf927c0..78d8a6829 100644 --- a/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/Response.kt +++ b/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/Response.kt @@ -6,19 +6,10 @@ import io.ktor.http.* /** An HTTP response. */ @Suppress("MemberVisibilityCanBePrivate") public class Response private constructor( - rawResponse: HttpResponse, - body: T?, - errorBody: Any?, + private val rawResponse: HttpResponse, + private val body: T?, + private val errorBody: Any?, ) { - private val rawResponse: HttpResponse - private val body: T? - private val errorBody: Any? - - init { - this.rawResponse = rawResponse - this.body = body - this.errorBody = errorBody - } /** The raw response from the HTTP client. */ public fun raw(): HttpResponse { diff --git a/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/internal/HandleDeprecatedConverters.kt b/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/internal/HandleDeprecatedConverters.kt new file mode 100644 index 000000000..06d18b7fc --- /dev/null +++ b/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/internal/HandleDeprecatedConverters.kt @@ -0,0 +1,71 @@ +package de.jensklingenberg.ktorfit.internal + +import de.jensklingenberg.ktorfit.Ktorfit +import io.ktor.client.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.util.reflect.* + +@OptIn(InternalKtorfitApi::class) +internal fun KtorfitClient.handleDeprecatedResponseConverters( + requestData: RequestData, + ktorfit: Ktorfit +): ReturnType? { + val returnTypeData = requestData.getTypeData() + + val requestTypeInfo = returnTypeData.typeArgs.firstOrNull()?.typeInfo ?: returnTypeData.typeInfo + + ktorfit.responseConverters.firstOrNull { converter -> + converter.supportedType( + returnTypeData, false + ) + }?.let { requestConverter -> + return requestConverter.wrapResponse( + typeData = returnTypeData, + requestFunction = { + try { + val data = + suspendRequest( + RequestData( + ktorfitRequestBuilder = requestData.ktorfitRequestBuilder, + returnTypeName = "io.ktor.client.statement.HttpResponse", + returnTypeInfo = typeInfo() + ) + ) + Pair(requestTypeInfo, data) + } catch (ex: Exception) { + throw ex + } + }, + ktorfit = ktorfit + ) as ReturnType? + } + + return null +} + +@OptIn(InternalKtorfitApi::class) +internal suspend fun handleDeprecatedSuspendResponseConverters( + requestData: RequestData, + httpClient: HttpClient, + ktorfit: Ktorfit +): ReturnType? { + val returnTypeData = requestData.getTypeData() + + val requestTypeInfo = returnTypeData.typeArgs.firstOrNull()?.typeInfo ?: returnTypeData.typeInfo + ktorfit.suspendResponseConverters.firstOrNull { converter -> + converter.supportedType( + returnTypeData, true + ) + }?.let { + return it.wrapSuspendResponse( + typeData = returnTypeData, + requestFunction = { + Pair(requestTypeInfo, httpClient.request { + requestData.ktorfitRequestBuilder(this) + }) + }, ktorfit + ) as ReturnType? + } + return null +} \ No newline at end of file diff --git a/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/internal/KtorfitClient.kt b/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/internal/KtorfitClient.kt index ad8fcd156..ca6b2fda4 100644 --- a/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/internal/KtorfitClient.kt +++ b/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/internal/KtorfitClient.kt @@ -26,30 +26,26 @@ internal class KtorfitClient(private val ktorfit: Ktorfit) : Client { ktorfit.nextResponseConverter(null, returnTypeData)?.let { responseConverter -> return responseConverter.convert { - try { - val data = - suspendRequest( - RequestData( - ktorfitRequestBuilder = requestData.ktorfitRequestBuilder, - returnTypeName = "io.ktor.client.statement.HttpResponse", - returnTypeInfo = typeInfo() - ) + val data = + suspendRequest( + RequestData( + ktorfitRequestBuilder = requestData.ktorfitRequestBuilder, + returnTypeName = "io.ktor.client.statement.HttpResponse", + returnTypeInfo = typeInfo() ) - data!! - } catch (ex: Exception) { - throw ex - } + ) + data!! } as ReturnType? } /** * Keeping this for compatibility */ - handleDeprecatedResponseConverters(requestData)?.let { + handleDeprecatedResponseConverters(requestData, ktorfit)?.let { return it } - val typeIsNullable = returnTypeData.isNullable + val typeIsNullable = returnTypeData.typeInfo.kotlinType?.isMarkedNullable ?: false return if (typeIsNullable) { null } else { @@ -92,7 +88,7 @@ internal class KtorfitClient(private val ktorfit: Ktorfit) : Client { /** * Keeping this for compatibility */ - handleDeprecatedSuspendResponseConverters(requestData)?.let { + handleDeprecatedSuspendResponseConverters(requestData, httpClient, ktorfit)?.let { return it } ?: DefaultSuspendResponseConverterFactory().suspendResponseConverter(returnTypeData, ktorfit).let { val response = httpClient.request { @@ -102,7 +98,7 @@ internal class KtorfitClient(private val ktorfit: Ktorfit) : Client { } } catch (exception: Exception) { - val typeIsNullable = returnTypeData.isNullable + val typeIsNullable = returnTypeData.typeInfo.kotlinType?.isMarkedNullable ?: false return if (typeIsNullable) { null } else { @@ -111,61 +107,6 @@ internal class KtorfitClient(private val ktorfit: Ktorfit) : Client { } } - private fun handleDeprecatedResponseConverters(requestData: RequestData): ReturnType? { - val returnTypeData = requestData.getTypeData() - - val requestTypeInfo = returnTypeData.typeArgs.firstOrNull()?.typeInfo ?: returnTypeData.typeInfo - - ktorfit.responseConverters.firstOrNull { converter -> - converter.supportedType( - returnTypeData, false - ) - }?.let { requestConverter -> - return requestConverter.wrapResponse( - typeData = returnTypeData, - requestFunction = { - try { - val data = - suspendRequest( - RequestData( - ktorfitRequestBuilder = requestData.ktorfitRequestBuilder, - returnTypeName = "io.ktor.client.statement.HttpResponse", - returnTypeInfo = typeInfo() - ) - ) - Pair(requestTypeInfo, data) - } catch (ex: Exception) { - throw ex - } - }, - ktorfit = ktorfit - ) as ReturnType? - } - - return null - } - - private suspend fun handleDeprecatedSuspendResponseConverters(requestData: RequestData): ReturnType? { - val returnTypeData = requestData.getTypeData() - - val requestTypeInfo = returnTypeData.typeArgs.firstOrNull()?.typeInfo ?: returnTypeData.typeInfo - ktorfit.suspendResponseConverters.firstOrNull { converter -> - converter.supportedType( - returnTypeData, true - ) - }?.let { - return it.wrapSuspendResponse( - typeData = returnTypeData, - requestFunction = { - Pair(requestTypeInfo, httpClient.request { - requestBuilder(requestData) - }) - }, ktorfit - ) as ReturnType? - } - return null - } - override fun convertParameterType(data: Any, parameterType: KClass<*>, requestType: KClass): T { ktorfit.nextRequestParameterConverter(null, parameterType, requestType)?.let { return requestType.cast(it.convert(data)) diff --git a/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/internal/RequestData.kt b/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/internal/RequestData.kt index c6eb03e2d..cc13121b6 100644 --- a/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/internal/RequestData.kt +++ b/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/internal/RequestData.kt @@ -10,10 +10,8 @@ import io.ktor.util.reflect.* @InternalKtorfitApi public data class RequestData( val ktorfitRequestBuilder: HttpRequestBuilder.() -> Unit = {}, - val returnTypeName: String, - val returnTypeInfo: TypeInfo + private val returnTypeName: String, + private val returnTypeInfo: TypeInfo ) { - internal fun getTypeData(): TypeData = createTypeData(returnTypeName, returnTypeInfo) - } diff --git a/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/internal/TypeData.kt b/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/internal/TypeData.kt index 3743ee257..888feccc6 100644 --- a/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/internal/TypeData.kt +++ b/ktorfit-lib-common/src/commonMain/kotlin/de/jensklingenberg/ktorfit/internal/TypeData.kt @@ -23,15 +23,14 @@ public data class TypeData( val typeArgument = qualifiedTypename.substringAfter("<").substringBeforeLast(">") val spilt = typeArgument.split(",") - val args = mutableListOf() - typeInfo.kotlinType?.arguments?.forEachIndexed { index, kTypeProjection -> + val args = typeInfo.kotlinType?.arguments?.mapIndexed { index, kTypeProjection -> val cleaned = spilt[index].trim() val modelKType = kTypeProjection.type val modelClass = (modelKType?.classifier as? KClass<*>?)!! - args.add(createTypeData(cleaned, TypeInfo(modelClass, modelKType.platformType, modelKType))) - } + createTypeData(cleaned, TypeInfo(modelClass, modelKType.platformType, modelKType)) + }.orEmpty() return TypeData(qualifiedTypename.substringBefore("<"), args, typeInfo = typeInfo) }