Skip to content

Commit

Permalink
Add streaming JsonConverter for retrofit
Browse files Browse the repository at this point in the history
This implements for Jake Wharton's converter as streaming variant (only
one direction). For now that's all we need.
JakeWharton/retrofit2-kotlinx-serialization-converter#43
  • Loading branch information
janseeger committed Jan 9, 2024
1 parent d63ff07 commit 44a92f0
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 1 deletion.
1 change: 1 addition & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions dachlatten-retrofit/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
plugins {
id("android-library-base")
id("android-library-unit-test")
id("android-library-release")
alias(libs.plugins.kotlin.serialization.plugin)
}

dependencies {
implementation(libs.kotlinx.serialization)
implementation(libs.retrofit.serialization)
implementation(libs.okhttp)

testImplementation(libs.kotlinx.serialization)
testImplementation(libs.okhttp.mockwebserver)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package de.sipgate.dachlatten.retrofit

import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import kotlinx.serialization.json.encodeToStream
import kotlinx.serialization.serializer
import okhttp3.MediaType
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.ResponseBody
import retrofit2.Converter
import retrofit2.Retrofit
import java.io.ByteArrayOutputStream
import java.lang.reflect.Type

@JvmName("convert")
@ExperimentalSerializationApi
fun Json.asConverterFactory(contentType: MediaType): Converter.Factory =
object : Converter.Factory() {
override fun responseBodyConverter(
type: Type,
annotations: Array<out Annotation>,
retrofit: Retrofit,
) = Converter<ResponseBody, Any> { value ->
this@asConverterFactory.decodeFromStream(
this@asConverterFactory.serializersModule.serializer(type),
value.byteStream(),
)
}

override fun requestBodyConverter(
type: Type,
parameterAnnotations: Array<out Annotation>,
methodAnnotations: Array<out Annotation>,
retrofit: Retrofit,
) = Converter<Any, RequestBody> { value ->
val stream = ByteArrayOutputStream()
this@asConverterFactory.encodeToStream(
this@asConverterFactory.serializersModule.serializer(type),
value,
stream,
)
stream.toByteArray().toRequestBody(
contentType,
0,
stream.size(),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package de.sipgate.dachlatten.retrofit

import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import mockwebserver3.MockResponse
import mockwebserver3.MockWebServer
import mockwebserver3.junit5.internal.MockWebServerExtension
import okhttp3.MediaType.Companion.toMediaType
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST

@OptIn(ExperimentalSerializationApi::class)
@ExtendWith(MockWebServerExtension::class)
class KotlinSerializationConverterFactoryStringTest {
private lateinit var service: Service

interface Service {
@GET("/")
fun deserialize(): Call<User>
@POST("/")
fun serialize(@Body user: User): Call<Void?>
}

@Serializable
data class User(val name: String)

@BeforeEach
fun setUp(server: MockWebServer) {
val contentType = "application/json; charset=utf-8".toMediaType()
val retrofit = Retrofit.Builder()
.baseUrl(server.url("/"))
.addConverterFactory(Json.asConverterFactory(contentType))
.build()
service = retrofit.create(Service::class.java)
}

@Test
fun deserialize(server: MockWebServer) {
server.enqueue(MockResponse().newBuilder().body("""{"name":"Bob"}""").build())
val user = service.deserialize().execute().body()!!
assertEquals(User("Bob"), user)
}

@Test
fun serialize(server: MockWebServer) {
server.enqueue(MockResponse())
service.serialize(User("Bob")).execute()
val request = server.takeRequest()
assertEquals("""{"name":"Bob"}""", request.body.readUtf8())
assertEquals("application/json; charset=utf-8", request.headers["Content-Type"])
}
}
10 changes: 9 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ robolectric = "4.10.3"
compose = "1.5.4"
compose-compiler = "1.5.5"
kotlinx-datetime = "0.5.0"
kotlinx-serialization = "1.6.2"
kover = "0.7.5"
mockk = "1.13.9"
androidx-activity = "1.8.2"
okhttp = "4.12.0"
retrofit-serialization = "1.0.0"

[libraries]
coroutines = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" }
Expand All @@ -35,15 +38,20 @@ kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-pl
kover-gradlePlugin = { group = "org.jetbrains.kotlinx.kover", name = "org.jetbrains.kotlinx.kover.gradle.plugin", version.ref = "kover" }
annotation-jvm = { group = "androidx.annotation", name = "annotation-jvm", version.ref = "annotation-jvm" }
kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime-jvm", version.ref = "kotlinx-datetime" }
kotlinx-serialization = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
okhttp-mockwebserver = { group = "com.squareup.okhttp3", name = "mockwebserver3-junit5", version.prefer = "5.0.0-alpha.12" }
retrofit-serialization = { group = "com.jakewharton.retrofit", name = "retrofit2-kotlinx-serialization-converter", version.ref = "retrofit-serialization" }

mockk-agent = { group = "io.mockk", name = "mockk-agent", version.ref = "mockk" }
mockk-jvm = { group = "io.mockk", name = "mockk-jvm", version.ref = "mockk" }

[bundles]
compose-ui-test = ["compose-ui-test-junit4", "compose-ui-test-manifest"]
mockk = ["mockk-agent", "mockk-jvm"]
mockk = ["mockk-agent", "mockk-jvm"]

[plugins]
androidLibrary = { id = "com.android.library", version.ref = "agp" }
kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
koverPlugin = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" }
kotlin-serialization-plugin = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ include(":dachlatten-debug")
include(":dachlatten-flow")
include(":dachlatten-google")
include(":dachlatten-primitives")
include(":dachlatten-retrofit")

0 comments on commit 44a92f0

Please sign in to comment.