Skip to content

Commit

Permalink
Add Tag annotation (#466)
Browse files Browse the repository at this point in the history
  • Loading branch information
Foso committed Oct 21, 2023
1 parent 4fa1a06 commit afca4c8
Show file tree
Hide file tree
Showing 17 changed files with 137 additions and 19 deletions.
3 changes: 3 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
13 changes: 13 additions & 0 deletions docs/requests.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<Post>
```

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:
Expand Down
4 changes: 4 additions & 0 deletions ktorfit-annotations/api/android/ktorfit-annotations.api
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
}

4 changes: 4 additions & 0 deletions ktorfit-annotations/api/jvm/ktorfit-annotations.api
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
}

Original file line number Diff line number Diff line change
@@ -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")

Original file line number Diff line number Diff line change
Expand Up @@ -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()
}


Expand Down Expand Up @@ -60,6 +61,10 @@ fun KSValueParameter.getParamAnnotationList(logger: KSPLogger): List<ParameterAn
paramAnnos.add(it)
}

ksValueParameter.getTagAnnotation()?.let {
paramAnnos.add(it)
}

ksValueParameter.getPathAnnotation()?.let {
if (ksValueParameter.type.resolve().isMarkedNullable) {
logger.error(KtorfitError.PATH_PARAMETER_TYPE_MAY_NOT_BE_NULLABLE, ksValueParameter.type)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package de.jensklingenberg.ktorfit.reqBuilderExtension

import de.jensklingenberg.ktorfit.model.ParameterData
import de.jensklingenberg.ktorfit.model.annotations.ParameterAnnotation

fun getAttributeCode(parameterDataList: List<ParameterData>): String {
return parameterDataList.filter { it.hasAnnotation<ParameterAnnotation.Tag>() }
.joinToString("\n") {
val tag = it.findAnnotationOrNull<ParameterAnnotation.Tag>()!!
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})"
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
}
"""
)
Expand Down
Original file line number Diff line number Diff line change
@@ -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))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit afca4c8

Please sign in to comment.