AniTrend / retrofit-graphql

Retrofit converter which uses annotations to inject .graphql query or mutation files into a request body, along with any variables.
https://anitrend.github.io/retrofit-graphql/
Apache License 2.0
109 stars 19 forks source link

Allow subclasses of QueryContainerBuilder for requests #116

Closed Cilenco closed 3 years ago

Cilenco commented 3 years ago

When defining the functions for our Graphql requests we have to put a QueryContainerBuilder as parameter which is a final class. If the related Graphql functions takes parameters as input we have to remember to pass these into the builder like this:

QueryContainerBuilder().putVariables(
    mapOf("arg1" to arg1, "arg2" to arg2)
)

It would be nice to let the type system take care of this. For this we have to pass a subclasses of QueryContainerBuilder into the functions where the constructor of that class takes care of the required arguments. Then we can define our functions like this:

class MyRequestArgs(arg1: Int, arg2: String): QueryContainerBuilder() { // Fails because QueryContainerBuilder is final
    init {
        putVariables(mapOf("arg1" to arg1, "arg2" to arg2))
    }
}

@POST("graphql") @GraphQuery("ItemRequest")
fun getItems(@Body builder: MyRequestArgs): ItemResponse

So in summary it would be great if QueryContainerBuilder could be an open class. However, I'm not entirely sure if this effects serialization and deserialization of request and response objects in any way.

wax911 commented 3 years ago

Thanks for the suggestion, I don't see any problem with opening up QueryContainerBuilder for extension. Out of curiosity though, have you ever tried creating your parameters in the form of a class that can produce a map?

For example this is how I usually go about it:

Contract

/**
 * Model query or mutation contract, enforces implementation of map conversion
 */
interface IGraphPayload {

    /**
     * A map serializer to build maps out of objects to allow easier consumption in a GraphQL API
     */
    fun toMap(): Map<String, Any?>
}

Implementation

data class CarouselQuery(
    val season: MediaSeason,
    val currentTime: Long,
    val scoreFormat: ScoreFormat? = null,
    val pageSize: Int
) : IGraphPayload {

    /**
     * A map serializer to build maps out of objects to allow easier consumption in a GraphQL API
     */
    override fun toMap() = mapOf(
        "season" to season,
        "currentTime" to currentTime,
        "scoreFormat" to scoreFormat,
        "perPage" to pageSize
    )
}

Usage

val query = CarouselQuery(...)
val queryContainerBuilder = QueryContainerBuilder()
queryContainerBuilder.putVariables(query.toMap())
wax911 commented 3 years ago

Since I have not gotten any feedback since the release of v0.11.0-alpha03 I am closing this issue for now, feel free to reopen it if you have any issues