model: switch from kotlinx to square

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
Harsh Shandilya 2020-12-17 11:15:13 +05:30
parent e93e6f0061
commit 3641cd81cd
No known key found for this signature in database
GPG key ID: 366D7BBAD1031E80
10 changed files with 109 additions and 110 deletions

View file

@ -1,16 +0,0 @@
package dev.msfjarvis.lobsters.data.api
import dev.msfjarvis.lobsters.data.api.LobstersApi.Companion.BASE_URL
import dev.msfjarvis.lobsters.model.LobstersPost
import io.ktor.client.HttpClient
import io.ktor.client.request.get
import javax.inject.Inject
/**
* Ktor backed implementation of [LobstersApi]
*/
class KtorLobstersApi @Inject constructor(private val client: HttpClient) : LobstersApi {
override suspend fun getHottestPosts(page: Int): List<LobstersPost> {
return client.get("${BASE_URL}/hottest.json?page=$page")
}
}

View file

@ -1,13 +1,16 @@
package dev.msfjarvis.lobsters.data.api package dev.msfjarvis.lobsters.data.api
import dev.msfjarvis.lobsters.model.LobstersPost import dev.msfjarvis.lobsters.model.LobstersPost
import retrofit2.http.GET
import retrofit2.http.Query
/** /**
* Simple interface defining an API for lobste.rs * Simple interface defining an API for lobste.rs
*/ */
interface LobstersApi { interface LobstersApi {
suspend fun getHottestPosts(page: Int): List<LobstersPost> @GET("hottest.json")
suspend fun getHottestPosts(@Query("page") page: Int): List<LobstersPost>
companion object { companion object {
const val BASE_URL = "https://lobste.rs" const val BASE_URL = "https://lobste.rs"

View file

@ -0,0 +1,44 @@
package dev.msfjarvis.lobsters.injection
import com.squareup.moshi.Moshi
import dagger.Module
import dagger.Lazy
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityComponent
import dev.msfjarvis.lobsters.data.api.LobstersApi
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.create
@Module
@InstallIn(ActivityComponent::class)
object ApiModule {
@Provides
fun provideClient(): OkHttpClient {
return OkHttpClient.Builder()
.build()
}
/**
* Using [Lazy] here is a trick I picked up from Zac Sweers, which he explained in more
* detail here: https://www.zacsweers.dev/dagger-party-tricks-deferred-okhttp-init/
*/
@Provides
fun provideRetrofit(
client: Lazy<OkHttpClient>,
moshi: Lazy<Moshi>,
): Retrofit {
return Retrofit.Builder()
.client(client.get())
.baseUrl(LobstersApi.BASE_URL)
.addConverterFactory(MoshiConverterFactory.create(moshi.get()))
.build()
}
@Provides
fun provideApi(retrofit: Retrofit): LobstersApi {
return retrofit.create()
}
}

View file

@ -1,14 +0,0 @@
package dev.msfjarvis.lobsters.injection
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityComponent
import dev.msfjarvis.lobsters.data.api.KtorLobstersApi
import dev.msfjarvis.lobsters.data.api.LobstersApi
@Module
@InstallIn(ActivityComponent::class)
abstract class KtorApiModule {
@Binds abstract fun bindLobstersApi(realApi: KtorLobstersApi): LobstersApi
}

View file

@ -1,26 +0,0 @@
package dev.msfjarvis.lobsters.injection
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import io.ktor.client.HttpClient
import io.ktor.client.engine.okhttp.OkHttp
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.json.serializer.KotlinxSerializer
@Module
@InstallIn(SingletonComponent::class)
object KtorClientModule {
@Provides
fun provideClient() = HttpClient(OkHttp) {
install(JsonFeature) {
serializer = KotlinxSerializer()
}
engine {
config {
followSslRedirects(true)
}
}
}
}

View file

@ -0,0 +1,16 @@
package dev.msfjarvis.lobsters.injection
import com.squareup.moshi.Moshi
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
object MoshiModule {
@Provides
fun provideMoshi(): Moshi {
return Moshi.Builder().build()
}
}

View file

@ -1,12 +1,12 @@
package dev.msfjarvis.lobsters.model package dev.msfjarvis.lobsters.model
import kotlinx.serialization.SerialName import com.squareup.moshi.Json
import kotlinx.serialization.Serializable import com.squareup.moshi.JsonClass
@Serializable @JsonClass(generateAdapter = true)
class KeybaseSignature( class KeybaseSignature(
@SerialName("kb_username") @Json(name = "kb_username")
val kbUsername: String, val kbUsername: String,
@SerialName("sig_hash") @Json(name = "sig_hash")
val sigHash: String val sigHash: String
) )

View file

@ -1,26 +1,26 @@
package dev.msfjarvis.lobsters.model package dev.msfjarvis.lobsters.model
import kotlinx.serialization.SerialName import com.squareup.moshi.Json
import kotlinx.serialization.Serializable import com.squareup.moshi.JsonClass
@Serializable @JsonClass(generateAdapter = true)
class LobstersPost( class LobstersPost(
@SerialName("short_id") @Json(name = "short_id")
val shortId: String, val shortId: String,
@SerialName("short_id_url") @Json(name = "short_id_url")
val shortIdUrl: String, val shortIdUrl: String,
@SerialName("created_at") @Json(name = "created_at")
val createdAt: String, val createdAt: String,
val title: String, val title: String,
val url: String, val url: String,
val score: Long, val score: Long,
val flags: Long, val flags: Long,
@SerialName("comment_count") @Json(name = "comment_count")
val commentCount: Long, val commentCount: Long,
val description: String, val description: String,
@SerialName("comments_url") @Json(name = "comments_url")
val commentsUrl: String, val commentsUrl: String,
@SerialName("submitter_user") @Json(name = "submitter_user")
val submitterUser: Submitter, val submitterUser: Submitter,
val tags: List<String>, val tags: List<String>,
) )

View file

@ -1,27 +1,27 @@
package dev.msfjarvis.lobsters.model package dev.msfjarvis.lobsters.model
import kotlinx.serialization.SerialName import com.squareup.moshi.Json
import kotlinx.serialization.Serializable import com.squareup.moshi.JsonClass
@Serializable @JsonClass(generateAdapter = true)
class Submitter( class Submitter(
val username: String, val username: String,
@SerialName("created_at") @Json(name = "created_at")
val createdAt: String, val createdAt: String,
@SerialName("is_admin") @Json(name = "is_admin")
val isAdmin: Boolean, val isAdmin: Boolean,
val about: String, val about: String,
@SerialName("is_moderator") @Json(name = "is_moderator")
val isModerator: Boolean, val isModerator: Boolean,
val karma: Long = 0, val karma: Long = 0,
@SerialName("avatar_url") @Json(name = "avatar_url")
val avatarUrl: String, val avatarUrl: String,
@SerialName("invited_by_user") @Json(name = "invited_by_user")
val invitedByUser: String, val invitedByUser: String,
@SerialName("github_username") @Json(name = "github_username")
val githubUsername: String? = null, val githubUsername: String? = null,
@SerialName("twitter_username") @Json(name = "twitter_username")
val twitterUsername: String? = null, val twitterUsername: String? = null,
@SerialName("keybase_signatures") @Json(name = "keybase_signatures")
val keybaseSignatures: List<KeybaseSignature> = emptyList() val keybaseSignatures: List<KeybaseSignature> = emptyList()
) )

View file

@ -1,54 +1,46 @@
package dev.msfjarvis.lobsters.data.api package dev.msfjarvis.lobsters.data.api
import dev.msfjarvis.lobsters.injection.ApiModule
import dev.msfjarvis.lobsters.injection.MoshiModule
import dev.msfjarvis.lobsters.util.TestUtils import dev.msfjarvis.lobsters.util.TestUtils
import io.ktor.client.HttpClient
import io.ktor.client.engine.mock.MockEngine
import io.ktor.client.engine.mock.respond
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.json.serializer.KotlinxSerializer
import io.ktor.http.fullPath
import io.ktor.http.headersOf
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import okhttp3.mockwebserver.Dispatcher
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import okhttp3.mockwebserver.RecordedRequest
import org.junit.AfterClass import org.junit.AfterClass
import org.junit.BeforeClass import org.junit.BeforeClass
import org.junit.Test import org.junit.Test
class KtorLobstersApiTest { class LobstersApiTest {
companion object { companion object {
@JvmStatic private val webServer = MockWebServer()
private lateinit var client: HttpClient private val apiData = TestUtils.getJson("hottest.json")
@JvmStatic private val okHttp = ApiModule.provideClient()
private lateinit var apiClient: LobstersApi private val retrofit = ApiModule.provideRetrofit(
{ okHttp },
{ MoshiModule.provideMoshi() }
)
private val apiClient = ApiModule.provideApi(retrofit)
@JvmStatic @JvmStatic
@BeforeClass @BeforeClass
fun setUp() { fun setUp() {
client = HttpClient(MockEngine) { webServer.start(8080)
install(JsonFeature) { webServer.dispatcher = object : Dispatcher() {
serializer = KotlinxSerializer() override fun dispatch(request: RecordedRequest): MockResponse {
return MockResponse().setBody(apiData).setResponseCode(200)
} }
engine {
addHandler { request ->
when (request.url.fullPath) {
"/hottest.json?page=1" -> {
val responseHeaders = headersOf("Content-Type" to listOf("application/json"))
respond(TestUtils.getJson("hottest.json"), headers = responseHeaders)
} }
else -> error("Unhandled ${request.url.fullPath}")
}
}
}
}
apiClient = KtorLobstersApi(client)
} }
@JvmStatic @JvmStatic
@AfterClass @AfterClass
fun tearDown() { fun tearDown() {
client.close() webServer.shutdown()
} }
} }