diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index a9a41692a..71a87c67c 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -9,6 +9,9 @@ But there is no intent to bump the Ktorfit major version for every KSP update. ## [Unreleased] ======================================== +### Added +- Add support for Tag annotation https://foso.github.io/Ktorfit/requests/#tag + ### Changed - Removed unnecessary safe call warnings from generated code for the Headers [#460](https://github.com/Foso/Ktorfit/issues/460) diff --git a/docs/requests.md b/docs/requests.md index 6c93ed793..d0f29b716 100644 --- a/docs/requests.md +++ b/docs/requests.md @@ -108,6 +108,19 @@ suspend fun signup( To send FormData you can use @Field or @FieldMap. Your function needs to be annotated with @FormUrlEncoded. +## Tag +Tag can be used to add a tag to a request. + +```kotlin +@GET("posts") +fun getPosts(@Tag("myTag") tag: String): List +``` + +You can then access the tag from the attributes of a Ktor HttpClientCall + +```kotlin +val myTag = response.call.attributes[AttributeKey("myTag")] +``` ## Multipart To send Multipart data you have two options: diff --git a/ktorfit-annotations/api/android/ktorfit-annotations.api b/ktorfit-annotations/api/android/ktorfit-annotations.api index 98df29bb5..98d529ae6 100644 --- a/ktorfit-annotations/api/android/ktorfit-annotations.api +++ b/ktorfit-annotations/api/android/ktorfit-annotations.api @@ -97,6 +97,10 @@ public abstract interface annotation class de/jensklingenberg/ktorfit/http/Reque public abstract interface annotation class de/jensklingenberg/ktorfit/http/Streaming : java/lang/annotation/Annotation { } +public abstract interface annotation class de/jensklingenberg/ktorfit/http/Tag : java/lang/annotation/Annotation { + public abstract fun value ()Ljava/lang/String; +} + public abstract interface annotation class de/jensklingenberg/ktorfit/http/Url : java/lang/annotation/Annotation { } diff --git a/ktorfit-annotations/api/jvm/ktorfit-annotations.api b/ktorfit-annotations/api/jvm/ktorfit-annotations.api index 98df29bb5..98d529ae6 100644 --- a/ktorfit-annotations/api/jvm/ktorfit-annotations.api +++ b/ktorfit-annotations/api/jvm/ktorfit-annotations.api @@ -97,6 +97,10 @@ public abstract interface annotation class de/jensklingenberg/ktorfit/http/Reque public abstract interface annotation class de/jensklingenberg/ktorfit/http/Streaming : java/lang/annotation/Annotation { } +public abstract interface annotation class de/jensklingenberg/ktorfit/http/Tag : java/lang/annotation/Annotation { + public abstract fun value ()Ljava/lang/String; +} + public abstract interface annotation class de/jensklingenberg/ktorfit/http/Url : java/lang/annotation/Annotation { } diff --git a/ktorfit-annotations/src/commonMain/kotlin/de/jensklingenberg/ktorfit/http/Tag.kt b/ktorfit-annotations/src/commonMain/kotlin/de/jensklingenberg/ktorfit/http/Tag.kt new file mode 100644 index 000000000..a97f20e8c --- /dev/null +++ b/ktorfit-annotations/src/commonMain/kotlin/de/jensklingenberg/ktorfit/http/Tag.kt @@ -0,0 +1,18 @@ +package de.jensklingenberg.ktorfit.http + +/** + * Adds the argument instance as a request tag using the type as a AttributeKey. + * + * ``` + * @GET("/") + * fun foo(@Tag tag: String) + * ``` + * + * Tag arguments may be `null` which will omit them from the request. + * + * @param value Will be used as the name for the attribute key. The default value will be replaced with the name of the parameter that is annotated. + * + */ +@Target(AnnotationTarget.VALUE_PARAMETER) +annotation class Tag(val value: String = "KTORFIT_DEFAULT_VALUE") + diff --git a/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/model/annotations/ParameterAnnotation.kt b/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/model/annotations/ParameterAnnotation.kt index 636c135a6..54d70d4ca 100644 --- a/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/model/annotations/ParameterAnnotation.kt +++ b/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/model/annotations/ParameterAnnotation.kt @@ -24,6 +24,7 @@ sealed class ParameterAnnotation{ data class FieldMap(val encoded: Boolean) : ParameterAnnotation() data class Part(val value: String = "", val encoding: String = "binary") : ParameterAnnotation() data class PartMap(val encoding: String = "binary") : ParameterAnnotation() + data class Tag(val value: String) : ParameterAnnotation() } @@ -60,6 +61,10 @@ fun KSValueParameter.getParamAnnotationList(logger: KSPLogger): List): String { + return parameterDataList.filter { it.hasAnnotation() } + .joinToString("\n") { + val tag = it.findAnnotationOrNull()!! + if (it.type.isNullable) { + "${it.name}?.let{ attributes.put(io.ktor.util.AttributeKey(\"${tag.value}\"), it) }" + } else { + "attributes.put(io.ktor.util.AttributeKey(\"${tag.value}\"), ${it.name})" + } + } +} + diff --git a/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/reqBuilderExtension/ReqBuilderExtensionNode.kt b/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/reqBuilderExtension/ReqBuilderExtensionNode.kt index 490f25934..c29e7328d 100644 --- a/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/reqBuilderExtension/ReqBuilderExtensionNode.kt +++ b/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/reqBuilderExtension/ReqBuilderExtensionNode.kt @@ -40,13 +40,16 @@ fun getReqBuilderExtensionText(functionData: FunctionData, resolver: Resolver): functionData.parameterDataList, listType, arrayType ) - val url = getUrlCode(functionData.parameterDataList, functionData.httpMethodAnnotation, queryCode) + val url = + getUrlCode(functionData.parameterDataList, functionData.httpMethodAnnotation, queryCode) val customReqBuilder = getCustomRequestBuilderText(functionData.parameterDataList) + val attributeKeys = getAttributeCode(functionData.parameterDataList) val args = listOf( method, url, body, headers, + attributeKeys, fields, parts, customReqBuilder diff --git a/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/utils/KSValueParameterExt.kt b/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/utils/KSValueParameterExt.kt index bb99a1dab..9dbabd0a4 100644 --- a/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/utils/KSValueParameterExt.kt +++ b/ktorfit-ksp/src/main/kotlin/de/jensklingenberg/ktorfit/utils/KSValueParameterExt.kt @@ -103,6 +103,13 @@ fun KSValueParameter.getBodyAnnotation(): Body? { } } +@OptIn(KspExperimental::class) +fun KSValueParameter.getTagAnnotation(): Tag? { + return this.getAnnotationsByType(de.jensklingenberg.ktorfit.http.Tag::class).firstOrNull()?.let { + return Tag(it.value.replace(KTORFIT_DEFAULT_VALUE, this.name.safeString())) + } +} + fun KSValueParameter.getRequestTypeAnnotation(): RequestType? { val requestTypeClazz = de.jensklingenberg.ktorfit.http.RequestType::class val filteredAnnotations = this.annotations.filter { diff --git a/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/HeaderAnnotationsTest.kt b/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/HeaderAnnotationsTest.kt index b299de89b..f3dcb3396 100644 --- a/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/HeaderAnnotationsTest.kt +++ b/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/HeaderAnnotationsTest.kt @@ -3,9 +3,9 @@ package de.jensklingenberg.ktorfit import com.tschuchort.compiletesting.KotlinCompilation import com.tschuchort.compiletesting.SourceFile import com.tschuchort.compiletesting.kspSourcesDir -import de.jensklingenberg.ktorfit.model.KtorfitError.Companion.HEADER_MAP_KEYS_MUST_BE_OF_TYPE_STRING -import de.jensklingenberg.ktorfit.model.KtorfitError.Companion.HEADER_MAP_PARAMETER_TYPE_MUST_BE_MAP -import org.junit.Assert.* +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue import org.junit.Test import java.io.File diff --git a/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/HeadersAnnotationsTest.kt b/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/HeadersAnnotationsTest.kt index f53031fdc..6323c07a5 100644 --- a/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/HeadersAnnotationsTest.kt +++ b/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/HeadersAnnotationsTest.kt @@ -3,9 +3,8 @@ package de.jensklingenberg.ktorfit import com.tschuchort.compiletesting.KotlinCompilation import com.tschuchort.compiletesting.SourceFile import com.tschuchort.compiletesting.kspSourcesDir -import de.jensklingenberg.ktorfit.model.KtorfitError.Companion.HEADER_MAP_KEYS_MUST_BE_OF_TYPE_STRING -import de.jensklingenberg.ktorfit.model.KtorfitError.Companion.HEADER_MAP_PARAMETER_TYPE_MUST_BE_MAP -import org.junit.Assert.* +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue import org.junit.Test import java.io.File diff --git a/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/QueryAnnotationsTest.kt b/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/QueryAnnotationsTest.kt index aaefd6996..5bbb579da 100644 --- a/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/QueryAnnotationsTest.kt +++ b/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/QueryAnnotationsTest.kt @@ -5,7 +5,6 @@ import com.tschuchort.compiletesting.KotlinCompilation import com.tschuchort.compiletesting.SourceFile import com.tschuchort.compiletesting.kspSourcesDir import de.jensklingenberg.ktorfit.model.KtorfitError -import org.junit.Assert import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Test diff --git a/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/ReqBuilderAnnotationsTest.kt b/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/ReqBuilderAnnotationsTest.kt index bca600fd9..5d489e96a 100644 --- a/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/ReqBuilderAnnotationsTest.kt +++ b/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/ReqBuilderAnnotationsTest.kt @@ -1,9 +1,14 @@ package de.jensklingenberg.ktorfit -import com.tschuchort.compiletesting.* +import com.tschuchort.compiletesting.KotlinCompilation +import com.tschuchort.compiletesting.SourceFile +import com.tschuchort.compiletesting.kspIncremental +import com.tschuchort.compiletesting.kspSourcesDir +import com.tschuchort.compiletesting.symbolProcessorProviders import de.jensklingenberg.ktorfit.model.KtorfitError -import org.junit.Assert -import org.junit.Assert.* +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue import org.junit.Test import java.io.File diff --git a/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/RequestTypeTest.kt b/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/RequestTypeTest.kt index 276c6e626..87c967cde 100644 --- a/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/RequestTypeTest.kt +++ b/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/RequestTypeTest.kt @@ -3,8 +3,8 @@ import com.tschuchort.compiletesting.KotlinCompilation import com.tschuchort.compiletesting.SourceFile import com.tschuchort.compiletesting.kspSourcesDir import de.jensklingenberg.ktorfit.getCompilation -import org.junit.Assert -import org.junit.Assert.* +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue import org.junit.Test import java.io.File @@ -19,10 +19,8 @@ class RequestTypeTest { import de.jensklingenberg.ktorfit.http.* interface TestService { - @GET("posts/{postId}/comments") suspend fun test(@RequestType(Int::class) @Path("postId") postId: String): String - } """ ) diff --git a/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/TagAnnotationsTest.kt b/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/TagAnnotationsTest.kt new file mode 100644 index 000000000..892033775 --- /dev/null +++ b/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/TagAnnotationsTest.kt @@ -0,0 +1,45 @@ +package de.jensklingenberg.ktorfit + +import com.tschuchort.compiletesting.KotlinCompilation +import com.tschuchort.compiletesting.SourceFile +import com.tschuchort.compiletesting.kspSourcesDir +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import java.io.File + +class TagAnnotationsTest { + + @Test + fun whenTagsAnnotationFound_AddThemAsAttributeKey() { + + val source = SourceFile.kotlin( + "Source.kt", """ + package com.example.api +import de.jensklingenberg.ktorfit.http.GET +import de.jensklingenberg.ktorfit.http.Tag + +interface TestService { + @GET("posts") + suspend fun test(@Tag myTag1 : String, @Tag("myTag2") someParameter: Int?): String +} + """ + ) + + val expectedHeadersArgumentText = + """attributes.put(io.ktor.util.AttributeKey("myTag1"), myTag1) + someParameter?.let{ attributes.put(io.ktor.util.AttributeKey("myTag2"), it) } """ + + val compilation = getCompilation(listOf(source)) + val result = compilation.compile() + assertEquals(KotlinCompilation.ExitCode.OK, result.exitCode) + val generatedSourcesDir = compilation.kspSourcesDir + val generatedFile = File( + generatedSourcesDir, + "/kotlin/com/example/api/_TestServiceImpl.kt" + ) + + val actualSource = generatedFile.readText() + assertTrue(actualSource.contains(expectedHeadersArgumentText)) + } +} diff --git a/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/reqBuilderExtension/GetBodyDataTextKtTest.kt b/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/reqBuilderExtension/GetBodyDataTextKtTest.kt index 1c87abfeb..be39aa654 100644 --- a/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/reqBuilderExtension/GetBodyDataTextKtTest.kt +++ b/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/reqBuilderExtension/GetBodyDataTextKtTest.kt @@ -3,8 +3,7 @@ package de.jensklingenberg.ktorfit.reqBuilderExtension import de.jensklingenberg.ktorfit.model.ParameterData import de.jensklingenberg.ktorfit.model.ReturnTypeData import de.jensklingenberg.ktorfit.model.annotations.ParameterAnnotation.Body -import org.junit.Assert -import org.junit.Assert.* +import org.junit.Assert.assertEquals import org.junit.Test class GetBodyDataTextKtTest { diff --git a/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/reqBuilderExtension/GetRequestBuilderTextKtTest.kt b/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/reqBuilderExtension/GetRequestBuilderTextKtTest.kt index 320deb7d9..74d259920 100644 --- a/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/reqBuilderExtension/GetRequestBuilderTextKtTest.kt +++ b/ktorfit-ksp/src/test/kotlin/de/jensklingenberg/ktorfit/reqBuilderExtension/GetRequestBuilderTextKtTest.kt @@ -3,8 +3,7 @@ package de.jensklingenberg.ktorfit.reqBuilderExtension import de.jensklingenberg.ktorfit.model.ParameterData import de.jensklingenberg.ktorfit.model.ReturnTypeData import de.jensklingenberg.ktorfit.model.annotations.ParameterAnnotation.RequestBuilder -import org.junit.Assert -import org.junit.Assert.* +import org.junit.Assert.assertEquals import org.junit.Test class GetRequestBuilderTextKtTest {