Skip to content

Commit

Permalink
[#18] Add Napier, fix network call on the wrong client
Browse files Browse the repository at this point in the history
  • Loading branch information
blyscuit committed Dec 9, 2022
1 parent fe54e53 commit 6cb3ac9
Show file tree
Hide file tree
Showing 18 changed files with 77 additions and 40 deletions.
3 changes: 3 additions & 0 deletions buildSrc/src/main/kotlin/appPackage/Dependency.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,7 @@ object Dependency {

// Turbine
const val TURBINE = "app.cash.turbine:turbine:${Version.TURBINE}"

// Napier
const val NAPIER = "io.github.aakira:napier:${Version.NAPIER}"
}
1 change: 1 addition & 0 deletions buildSrc/src/main/kotlin/appPackage/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ object Version {
const val SECURITY = "1.1.0-alpha03"
const val MOKO_RESOURCES = "0.20.1"
const val TURBINE = "0.12.1"
const val NAPIER = "2.6.1"
}
1 change: 1 addition & 0 deletions shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ kotlin {
implementation(Dependency.KOTEST_PROPERTY)
implementation(Dependency.MULTIPLATFORM_SETTINGS)
implementation(Dependency.MULTIPLATFORM_SETTINGS_SERIALIZATION)
implementation(Dependency.NAPIER)
}
}
val commonTest by getting {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package co.nimblehq.blisskmmic.data.network.core

import co.nimblehq.jsonapi.json.JsonApi
import io.github.aakira.napier.DebugAntilog
import io.github.aakira.napier.Napier
import io.ktor.client.*
import io.ktor.client.engine.*
import io.ktor.client.plugins.contentnegotiation.*
Expand All @@ -23,6 +25,8 @@ open class NetworkClient {
}

constructor(engine: HttpClientEngine? = null) {
Napier.takeLogarithm()
Napier.base(DebugAntilog())
if (engine == null) {
client = HttpClient(clientConfig())
} else {
Expand Down Expand Up @@ -50,7 +54,14 @@ open class NetworkClient {
}
open fun clientConfig(): HttpClientConfig<*>.() -> Unit {
return {
install(Logging)
install(Logging) {
level = LogLevel.ALL
logger = object : Logger {
override fun log(message: String) {
Napier.log(io.github.aakira.napier.LogLevel.DEBUG, message = message)
}
}
}
install(ContentNegotiation) {
json(json)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import kotlinx.coroutines.flow.singleOrNull

class TokenizedNetworkClient: NetworkClient {

val localDataSource: LocalDataSource
private val localDataSource: LocalDataSource

constructor(
engine: HttpClientEngine? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,30 @@ import co.nimblehq.blisskmmic.data.network.target.LoginTargetType
import co.nimblehq.blisskmmic.data.network.target.ResetPasswordTargetType
import co.nimblehq.blisskmmic.data.model.PaginationMetaApiModel
import co.nimblehq.blisskmmic.data.model.SurveyApiModel
import co.nimblehq.blisskmmic.data.network.core.NetworkClient
import co.nimblehq.blisskmmic.data.network.target.LoginTargetType
import co.nimblehq.blisskmmic.data.network.helpers.requestBuilder
import co.nimblehq.blisskmmic.data.network.target.SurveySelectionTargetType
import co.nimblehq.blisskmmic.data.network.target.SurveyTargetType
import co.nimblehq.blisskmmic.domain.model.PaginationMeta
import co.nimblehq.blisskmmic.domain.model.Survey
import co.nimblehq.blisskmmic.domain.model.TokenApiModel
import kotlinx.coroutines.flow.Flow

interface NetworkDataSource {

fun logIn(target: LoginTargetType): Flow<TokenApiModel>
fun resetPassword(target: ResetPasswordTargetType): Flow<ResetPasswordMeta>
fun survey(target: SurveyTargetType): Flow<Pair<List<SurveyApiModel>, PaginationMetaApiModel>>
fun survey(target: SurveySelectionTargetType): Flow<Pair<List<SurveyApiModel>, PaginationMetaApiModel>>
}

class NetworkDataSourceImpl(private val networkClient: NetworkClient): NetworkDataSource {

override fun logIn(target: LoginTargetType): Flow<TokenApiModel> {
return networkClient.fetch(target.requestBuilder)
return networkClient.fetch(target.requestBuilder())
}

override fun resetPassword(target: ResetPasswordTargetType): Flow<ResetPasswordMeta> {
return networkClient.fetch(target.requestBuilder)
return networkClient.fetch(target.requestBuilder())
}
override fun survey(target: SurveyTargetType): Flow<Pair<List<SurveyApiModel>, PaginationMetaApiModel>> {
return networkClient.fetchWithMeta(target.requestBuilder)

override fun survey(target: SurveySelectionTargetType):
Flow<Pair<List<SurveyApiModel>, PaginationMetaApiModel>> {
return networkClient.fetchWithMeta(target.requestBuilder())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,35 @@ package co.nimblehq.blisskmmic.data.network.helpers
import co.nimblehq.blisskmmic.BuildKonfig
import io.ktor.client.request.*
import io.ktor.http.*
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.json.jsonObject

interface TargetType {
interface TargetType<T> {

val path: String
val method: HttpMethod
val body: Any?
val headers: Map<String, String>?
val body: T?
}

val requestBuilder: HttpRequestBuilder
get() {
val builder = HttpRequestBuilder()
builder.url("${BuildKonfig.BASE_URL}$API_VERSION$path")
builder.method = method
builder.contentType(ContentType.Application.Json)
internal inline fun <reified T> TargetType<T>.requestBuilder(): HttpRequestBuilder {
val builder = HttpRequestBuilder()
builder.url("${BuildKonfig.BASE_URL}$API_VERSION$path")
builder.method = method
builder.contentType(ContentType.Application.Json)
if (method == HttpMethod.Get) {
builder.setQueryParameters(body)
} else {
builder.setBody(body)
return builder
}
return builder
}

private inline fun <reified T> HttpRequestBuilder.setQueryParameters(parameters: T?) {
parameters?.run {
val queryParameters = Json.encodeToJsonElement(this).jsonObject
for ((key, value) in queryParameters) {
parameter(key, value)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import io.ktor.http.*
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

sealed class SurveyTargetType: TargetType
sealed class SurveyTargetType<T>: TargetType<T>

class SurveySelectionTargetType(page: Int = 1, size: Int = 3): SurveyTargetType() {
class SurveySelectionTargetType(page: Int = 1, size: Int = 3):
SurveyTargetType<SurveySelectionTargetType.SurveySelectionInput>() {

@Serializable
data class SurveySelectionInput(
Expand All @@ -18,7 +19,6 @@ class SurveySelectionTargetType(page: Int = 1, size: Int = 3): SurveyTargetType(
)

override var path = "surveys"
override var method = HttpMethod.Post
override var method = HttpMethod.Get
override var body = SurveySelectionInput(page, size)
override var headers: Map<String, String>? = null
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import io.ktor.http.*
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

sealed class UserTargetType: TargetType
sealed class UserTargetType<T>: TargetType<T>

class LoginTargetType(email: String, password: String): UserTargetType() {
class LoginTargetType(email: String, password: String):
UserTargetType<LoginTargetType.LoginInput>() {

@Serializable
data class LoginInput(
Expand All @@ -31,11 +32,10 @@ class LoginTargetType(email: String, password: String): UserTargetType() {
BuildKonfig.CLIENT_ID,
BuildKonfig.CLIENT_SECRET
)
override val headers: Map<String, String>? = null
}


class ResetPasswordTargetType(email: String): UserTargetType() {
class ResetPasswordTargetType(email: String): UserTargetType<ResetPasswordTargetType.ResetPasswordInput>() {

@Serializable
data class User(
Expand All @@ -58,5 +58,4 @@ class ResetPasswordTargetType(email: String): UserTargetType() {
BuildKonfig.CLIENT_ID,
BuildKonfig.CLIENT_SECRET
)
override var headers: Map<String, String>? = null
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package co.nimblehq.blisskmmic.data.repository

import co.nimblehq.blisskmmic.data.model.*
import co.nimblehq.blisskmmic.data.network.core.NetworkClient
import co.nimblehq.blisskmmic.data.network.datasource.NetworkDataSource
import co.nimblehq.blisskmmic.data.network.target.SurveySelectionTargetType
import co.nimblehq.blisskmmic.domain.model.PaginationMeta
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
package co.nimblehq.blisskmmic.di.koin.constants

const val TOKENIZED_NETWORK_CLIENT_KOIN = "TOKENIZED_NETWORK_CLIENT_KOIN"
const val NETWORK_CLIENT_KOIN = "NETWORK_CLIENT_KOIN"
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ import co.nimblehq.blisskmmic.data.network.core.NetworkClient
import co.nimblehq.blisskmmic.data.network.core.TokenizedNetworkClient
import co.nimblehq.blisskmmic.data.network.datasource.NetworkDataSource
import co.nimblehq.blisskmmic.data.network.datasource.NetworkDataSourceImpl
import co.nimblehq.blisskmmic.di.koin.constants.NETWORK_CLIENT_KOIN
import co.nimblehq.blisskmmic.di.koin.constants.TOKENIZED_NETWORK_CLIENT_KOIN
import org.koin.core.qualifier.named
import org.koin.dsl.module

val networkModule = module {
single { NetworkClient() }
single<NetworkDataSource> { NetworkDataSourceImpl(get()) }
factory(named(TOKENIZED_NETWORK_CLIENT_KOIN)) { TokenizedNetworkClient(null, get()) }
single<NetworkDataSource>(named(NETWORK_CLIENT_KOIN)) { NetworkDataSourceImpl(get()) }
factory<NetworkDataSource>(named(TOKENIZED_NETWORK_CLIENT_KOIN)) {
NetworkDataSourceImpl(TokenizedNetworkClient(null, get()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@ package co.nimblehq.blisskmmic.di.koin.modules
import co.nimblehq.blisskmmic.data.repository.AccountRecoveryRepositoryImpl
import co.nimblehq.blisskmmic.domain.repository.AccountRecoveryRepository
import co.nimblehq.blisskmmic.data.repository.AuthenticationRepositoryImpl
import co.nimblehq.blisskmmic.data.repository.SurveyRepositoryImpl
import co.nimblehq.blisskmmic.domain.repository.AuthenticationRepository
import co.nimblehq.blisskmmic.di.koin.constants.NETWORK_CLIENT_KOIN
import co.nimblehq.blisskmmic.di.koin.constants.TOKENIZED_NETWORK_CLIENT_KOIN
import co.nimblehq.blisskmmic.domain.repository.SurveyRepository
import org.koin.core.qualifier.named
import org.koin.dsl.module

val repositoryModule = module {
single<AuthenticationRepository> { AuthenticationRepositoryImpl(get(), get()) }
single<AccountRecoveryRepository> { AccountRecoveryRepositoryImpl(get()) }
single<AuthenticationRepository> { AuthenticationRepositoryImpl(get(named(NETWORK_CLIENT_KOIN)), get()) }
single<AccountRecoveryRepository> { AccountRecoveryRepositoryImpl(get(named(NETWORK_CLIENT_KOIN))) }
factory<SurveyRepository> { SurveyRepositoryImpl(get(named(TOKENIZED_NETWORK_CLIENT_KOIN))) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import co.nimblehq.blisskmmic.domain.usecase.LogInUseCase
import co.nimblehq.blisskmmic.domain.usecase.LogInUseCaseImpl
import co.nimblehq.blisskmmic.domain.usecase.ResetPasswordUseCase
import co.nimblehq.blisskmmic.domain.usecase.ResetPasswordUseCaseImpl
import co.nimblehq.blisskmmic.domain.usecase.SurveyListUseCase
import co.nimblehq.blisskmmic.domain.usecase.SurveyListUseCaseImpl
import org.koin.dsl.module

val useCaseModule = module {
single<LogInUseCase> { LogInUseCaseImpl(get()) }
single<ResetPasswordUseCase> { ResetPasswordUseCaseImpl(get()) }
factory<SurveyListUseCase> { SurveyListUseCaseImpl(get()) }
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package co.nimblehq.blisskmmic.domain.repository

import co.nimblehq.blisskmmic.data.database.model.TokenDatabaseModel
import co.nimblehq.blisskmmic.domain.model.Token
import kotlinx.coroutines.flow.Flow

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,6 @@ class TokenizedNetworkClientTest {

@Test
fun `when calling fetchWithMeta, it returns correct object`() = runTest {
val mocker = Mocker()
val localDataSource = MockLocalDataSource(mocker)
mocker.every {
localDataSource.getToken()
} returns flow { emit(token) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import co.nimblehq.blisskmmic.domain.model.fakeSurvey
import co.nimblehq.blisskmmic.domain.repository.MockSurveyRepository
import co.nimblehq.blisskmmic.domain.repository.SurveyRepository
import io.kotest.matchers.shouldBe
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.test.runTest
Expand All @@ -17,6 +18,7 @@ import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.fail

@OptIn(ExperimentalCoroutinesApi::class)
@UsesMocks(SurveyRepository::class)
@UsesFakes(Survey::class, PaginationMeta::class)
class SurveyListUseCaseTest {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package co.nimblehq.blisskmmic.helpers.mock.ktor
import co.nimblehq.blisskmmic.data.network.helpers.API_VERSION

fun apiPath(fullPath: String): String {
return fullPath.split(API_VERSION).last()
return fullPath.split(API_VERSION).last().split("?").first()
}

0 comments on commit 6cb3ac9

Please sign in to comment.