Skip to content

Commit

Permalink
Ktor (#234)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelbel committed Feb 27, 2024
1 parent 212d080 commit ad8fd43
Show file tree
Hide file tree
Showing 27 changed files with 371 additions and 130 deletions.
6 changes: 3 additions & 3 deletions core/network/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ android {

dependencies {
implementation(libs.androidx.startup.runtime)
api(libs.kotlin.serialization.json)
implementation(libs.kotlin.serialization.json)
implementation(libs.okhttp.logging.interceptor)
implementation(libs.retrofit.converter.serialization)
api(libs.retrofit)
implementation(libs.bundles.retrofit)
implementation(libs.bundles.ktor)
debugImplementation(libs.chucker.library)
releaseImplementation(libs.chucker.library.no.op)
debugImplementation(libs.flaker.android.okhttp)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.michaelbel.movies.network

const val TMDB_API_ENDPOINT = "https://api.themoviedb.org/3/"
const val TMDB_URL = "https://themoviedb.org"
const val TMDB_TERMS_OF_USE = "$TMDB_URL/documentation/website/terms-of-use"
const val TMDB_PRIVACY_POLICY = "$TMDB_URL/privacy-policy"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.michaelbel.movies.network.ktor

import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.client.request.parameter
import javax.inject.Inject
import org.michaelbel.movies.network.model.Account

class KtorAccountService @Inject constructor(
private val ktorHttpClient: HttpClient
) {

suspend fun accountDetails(
sessionId: String
): Account {
return ktorHttpClient.get("account") {
parameter("session_id", sessionId)
}.body()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.michaelbel.movies.network.ktor

import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.delete
import io.ktor.client.request.get
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import javax.inject.Inject
import org.michaelbel.movies.network.model.DeletedSession
import org.michaelbel.movies.network.model.RequestToken
import org.michaelbel.movies.network.model.Session
import org.michaelbel.movies.network.model.SessionRequest
import org.michaelbel.movies.network.model.Token
import org.michaelbel.movies.network.model.Username

class KtorAuthenticationService @Inject constructor(
private val ktorHttpClient: HttpClient
) {

suspend fun createRequestToken(): Token {
return ktorHttpClient.get("authentication/token/new?").body()
}

suspend fun createSessionWithLogin(
username: Username
): Token {
return ktorHttpClient.post("authentication/token/validate_with_login?") {
setBody(username)
}.body()
}

suspend fun createSession(
authToken: RequestToken
): Session {
return ktorHttpClient.post("authentication/session/new?") {
setBody(authToken)
}.body()
}

suspend fun deleteSession(
sessionRequest: SessionRequest
): DeletedSession {
return ktorHttpClient.delete("authentication/session?") {
setBody(sessionRequest)
}.body()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.michaelbel.movies.network.ktor

import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.client.request.parameter
import javax.inject.Inject
import org.michaelbel.movies.network.model.ImagesResponse
import org.michaelbel.movies.network.model.Movie
import org.michaelbel.movies.network.model.MovieResponse
import org.michaelbel.movies.network.model.Result

class KtorMovieService @Inject constructor(
private val ktorHttpClient: HttpClient
) {

suspend fun movies(
list: String,
language: String,
page: Int
): Result<MovieResponse> {
return ktorHttpClient.get("movie/$list") {
parameter("language", language)
parameter("page", page)
}.body()
}

suspend fun movie(
movieId: Int,
language: String
): Movie {
return ktorHttpClient.get("movie/$movieId") {
parameter("language", language)
}.body()
}

suspend fun images(
movieId: Int
): ImagesResponse {
return ktorHttpClient.get("movie/$movieId/images").body()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.michaelbel.movies.network.ktor

import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.client.request.parameter
import javax.inject.Inject
import org.michaelbel.movies.network.model.MovieResponse
import org.michaelbel.movies.network.model.Result

class KtorSearchService @Inject constructor(
private val ktorHttpClient: HttpClient
) {

suspend fun searchMovies(
query: String,
language: String,
page: Int
): Result<MovieResponse> {
return ktorHttpClient.get("search/movie") {
parameter("query", query)
parameter("language", language)
parameter("page", page)
}.body()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package org.michaelbel.movies.network.ktor.di

import com.chuckerteam.chucker.api.ChuckerInterceptor
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import io.github.rotbolt.flakerokhttpcore.FlakerInterceptor
import io.ktor.client.HttpClient
import io.ktor.client.engine.okhttp.OkHttp
import io.ktor.client.plugins.HttpTimeout
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.defaultRequest
import io.ktor.http.ContentType
import io.ktor.http.contentType
import io.ktor.serialization.kotlinx.json.json
import javax.inject.Singleton
import kotlinx.serialization.json.Json
import okhttp3.logging.HttpLoggingInterceptor
import org.michaelbel.movies.network.TMDB_API_ENDPOINT
import org.michaelbel.movies.network.okhttp.di.OkhttpModule
import org.michaelbel.movies.network.okhttp.interceptor.ApikeyInterceptor

@Module
@InstallIn(SingletonComponent::class)
internal object KtorModule {

@Provides
@Singleton
fun provideKtorHttpClient(
chuckerInterceptor: ChuckerInterceptor,
flakerInterceptor: FlakerInterceptor,
httpLoggingInterceptor: HttpLoggingInterceptor,
apikeyInterceptor: ApikeyInterceptor
): HttpClient {
val ktor = HttpClient(OkHttp) {
defaultRequest {
contentType(ContentType.Application.Json)
url(TMDB_API_ENDPOINT)
}
install(ContentNegotiation) {
json(
Json {
ignoreUnknownKeys = true
}
)
}
install(HttpTimeout) {
requestTimeoutMillis = REQUEST_TIMEOUT_MILLIS
connectTimeoutMillis = OkhttpModule.CONNECT_TIMEOUT_MILLIS
socketTimeoutMillis = SOCKET_TIMEOUT_SECONDS
}
engine {
clientCacheSize = OkhttpModule.HTTP_CACHE_SIZE_BYTES
config {
addInterceptor(chuckerInterceptor)
addInterceptor(flakerInterceptor)
addInterceptor(httpLoggingInterceptor)
addInterceptor(apikeyInterceptor)
}
}
}
return ktor
}

private const val REQUEST_TIMEOUT_MILLIS = 10_000L
private const val SOCKET_TIMEOUT_SECONDS = 10_000L
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.michaelbel.movies.network.okhttp
package org.michaelbel.movies.network.okhttp.di

import android.content.Context
import com.chuckerteam.chucker.api.ChuckerInterceptor
Expand All @@ -25,7 +25,7 @@ internal object OkhttpModule {
fun httpCache(
@ApplicationContext context: Context
): Cache {
return Cache(context.cacheDir, HTTP_CACHE_SIZE_BYTES)
return Cache(context.cacheDir, HTTP_CACHE_SIZE_BYTES.toLong())
}

@Provides
Expand Down Expand Up @@ -56,10 +56,10 @@ internal object OkhttpModule {
addInterceptor(flakerInterceptor)
addInterceptor(httpLoggingInterceptor)
addInterceptor(apikeyInterceptor)
callTimeout(CALL_TIMEOUT_SECONDS, TimeUnit.SECONDS)
connectTimeout(CONNECT_TIMEOUT_SECONDS, TimeUnit.SECONDS)
readTimeout(READ_TIMEOUT_SECONDS, TimeUnit.SECONDS)
writeTimeout(WRITE_TIMEOUT_SECONDS, TimeUnit.SECONDS)
callTimeout(CALL_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
connectTimeout(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
readTimeout(READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
writeTimeout(WRITE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
retryOnConnectionFailure(true)
followRedirects(true)
followSslRedirects(true)
Expand All @@ -68,9 +68,9 @@ internal object OkhttpModule {
return builder.build()
}

private const val HTTP_CACHE_SIZE_BYTES = 1024 * 1024 * 50L
private const val CALL_TIMEOUT_SECONDS = 0L
private const val CONNECT_TIMEOUT_SECONDS = 10L
private const val READ_TIMEOUT_SECONDS = 10L
private const val WRITE_TIMEOUT_SECONDS = 10L
const val HTTP_CACHE_SIZE_BYTES = 1024 * 1024 * 50
const val CONNECT_TIMEOUT_MILLIS = 10_000L
private const val READ_TIMEOUT_MILLIS = 10_000L
private const val WRITE_TIMEOUT_MILLIS = 10_000L
private const val CALL_TIMEOUT_MILLIS = 0L
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package org.michaelbel.movies.network.service.account
package org.michaelbel.movies.network.retrofit

import org.michaelbel.movies.network.model.Account
import retrofit2.http.GET
import retrofit2.http.Query

interface AccountService {
@Deprecated("Use KtorAccountService instead", ReplaceWith("KtorAccountService"))
interface RetrofitAccountService {

@GET("account")
suspend fun accountDetails(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.michaelbel.movies.network.service.authentication
package org.michaelbel.movies.network.retrofit

import org.michaelbel.movies.network.model.DeletedSession
import org.michaelbel.movies.network.model.RequestToken
Expand All @@ -11,7 +11,8 @@ import retrofit2.http.GET
import retrofit2.http.HTTP
import retrofit2.http.POST

interface AuthenticationService {
@Deprecated("Use KtorAuthenticationService instead", ReplaceWith("KtorAuthenticationService"))
interface RetrofitAuthenticationService {

@GET("authentication/token/new?")
suspend fun createRequestToken(): Token
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.michaelbel.movies.network.service.movie
package org.michaelbel.movies.network.retrofit

import org.michaelbel.movies.network.model.ImagesResponse
import org.michaelbel.movies.network.model.Movie
Expand All @@ -8,7 +8,8 @@ import retrofit2.http.GET
import retrofit2.http.Path
import retrofit2.http.Query

interface MovieService {
@Deprecated("Use KtorMovieService instead", ReplaceWith("KtorMovieService"))
interface RetrofitMovieService {

@GET("movie/{list}")
suspend fun movies(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package org.michaelbel.movies.network.service.search
package org.michaelbel.movies.network.retrofit

import org.michaelbel.movies.network.model.MovieResponse
import org.michaelbel.movies.network.model.Result
import retrofit2.http.GET
import retrofit2.http.Query

interface SearchService {
@Deprecated("Use KtorSearchService instead", ReplaceWith("KtorSearchService"))
interface RetrofitSearchService {

@GET("search/movie")
suspend fun searchMovies(
Expand Down

0 comments on commit ad8fd43

Please sign in to comment.