Skip to content

Annotation to transform query parameter #3321

Open
@christophehenry

Description

@christophehenry

What kind of issue is this?

  • Question. This issue tracker is not the place for questions. If you want to ask how to do
    something, or to understand why something isn't working the way you expect it to, use Stack
    Overflow. https://stackoverflow.com/questions/tagged/retrofit

  • Bug report. If you’ve found a bug, spend the time to write a failing test. Bugs with tests
    get fixed. Here’s an example: https://gist.github.com/swankjesse/6608b4713ad80988cdc9

  • Feature Request. Start by telling us what problem you’re trying to solve. Often a solution
    already exists! Don’t send pull requests to implement new features without first getting our
    support. Sometimes we leave features out on purpose to keep the project small.

there are cases when @Query is not enough and you would like to perform more sophisticated computations of query based on the method parameter. I have a use case in a feed reader implementing the defunct Google reader API. If you want to mark an article as read, you must hit the endpoint reader/api/0/edit-tag with query parameter a set to user/-/state/com.google/read. If you want to mark it as undread, though, you must use query parameter r.

Here is a rough implementation I made to solve that case using an interceptor:

@Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class QueryTransformer(val value: KClass<out QTransformer<out Any>>)

interface QTransformer<T> {
    fun transform(param: T): List<Pair<String, String>>
}

class APIInterceptor(): Interceptor {
    lateinit var service: API

    override fun intercept(chain: Interceptor.Chain): OkHttp3Response {
        val request = chain.request()
        val invocation = request.tag(Invocation::class.java)!!
        val method = invocation.method()
        val builder = request.newBuilder()
        var reqUrl = request.url()

        method.parameterAnnotations.forEachIndexed {index, annotations ->
            val annotation = annotations.find {it is QueryTransformer}
            if(annotation != null && annotation is QueryTransformer) {
                val parameter = invocation.arguments()[index]!!
                val parameterType = method.parameterTypes[index]!!
                val transformer = annotation.value.primaryConstructor!!.call()

                @Suppress("UNCHECKED_CAST")
                val queryParams = transformer.javaClass.getMethod("transform", parameterType)
                    .invoke(transformer, parameter) as List<Pair<String, String>>
                queryParams.forEach {
                    reqUrl = reqUrl.newBuilder().setQueryParameter(it.first, it.second).build()
                }
            }
        }

        return builder.url(reqUrl).build().let {chain.proceed(it)}
    }
}

It would be nice to have it natively in Retrofit because currenlty, this implementation forces me to use it in combination with @Query.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions