Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adicionando teste a Repositórios #18

Merged
merged 14 commits into from
Aug 26, 2022
Merged
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.github.felipecastilhos.pokedexandroid.core.datasource

import com.github.felipecastilhos.pokedexandroid.core.datasource.remote.DataSourceException
import com.github.felipecastilhos.pokedexandroid.core.datasource.remote.DataSourceError

/**
* Resource is an abstraction for data fetch
Expand All @@ -16,7 +16,7 @@ sealed class Resource<out T> {
* Resource data fetch didn't work
* @param exception was the error that occurred fetching data
*/
data class Error(val exception: DataSourceException) : Resource<Nothing>()
data class Error(val exception: DataSourceError) : Resource<Nothing>()

/**
* Resource is loading, none result was returned yet.
Expand All @@ -35,7 +35,7 @@ inline fun <T : Any> Resource<T>.onSuccess(action: (T) -> Unit): Resource<T> {
/**
* Extension to handle resouce on error data fetch
*/
inline fun <T : Any> Resource<T>.onError(action: (DataSourceException) -> Unit): Resource<T> {
inline fun <T : Any> Resource<T>.onError(action: (DataSourceError) -> Unit): Resource<T> {
if (this is Resource.Error) action(exception)
return this
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ import com.apollographql.apollo.api.Error
* Data source exception
* @param messageResource message for the error
*/
sealed class DataSourceException(
sealed class DataSourceError(
val messageResource: Any?
) : RuntimeException() {
) {
/**
* Unexpected error
* @param messageResource resource for the message
*/
class Unexpected(messageResource: Int) : DataSourceException(messageResource)
class Unexpected(messageResource: Int) : DataSourceError(messageResource)

/**
* Server error
* @param error that occurred
*/
class Server(error: Error?) : DataSourceException(error)
class Server(error: Error?) : DataSourceError(error)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,23 @@ package com.github.felipecastilhos.pokedexandroid.core.network
import com.apollographql.apollo.api.Response
import com.github.felipecastilhos.pokedexandroid.R
import com.github.felipecastilhos.pokedexandroid.core.datasource.Resource
import com.github.felipecastilhos.pokedexandroid.core.datasource.remote.DataSourceException
import com.github.felipecastilhos.pokedexandroid.core.datasource.remote.DataSourceError

/**
* Maps apollo client response to datasource's resource abstraction
* @param dataTo is a map to the response data into resource data
*/
fun <T, V> Response<T>?.toResource(dataTo: (T?) -> V): Resource<V?> {
return try {
when {
this == null -> {
Resource.Error(DataSourceException.Unexpected(R.string.server_unexpected_error))
}
this.hasErrors() -> {
Resource.Error(DataSourceException.Server(errors?.first()))
}
else -> {
Resource.Success(dataTo(data))
}
return when {
this == null -> {
Resource.Error(DataSourceError.Unexpected(R.string.server_unexpected_error))
}
this.hasErrors() -> {
Resource.Error(DataSourceError.Server(errors?.first()))
}
else -> {
Resource.Success(dataTo(data))
}
} catch (e: Exception) {
Resource.Error(DataSourceException.Unexpected(R.string.server_unexpected_error))
}
}
}
felipecastilhos marked this conversation as resolved.
Show resolved Hide resolved
felipecastilhos marked this conversation as resolved.
Show resolved Hide resolved
felipecastilhos marked this conversation as resolved.
Show resolved Hide resolved

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.felipecastilhos.pokedexandroid.features.home.domain.repository
package com.github.felipecastilhos.pokedexandroid.features.home.data.datasource

import com.github.felipecastilhos.pokedexandroid.GetPokemonQuery
import com.github.felipecastilhos.pokedexandroid.core.logs.LogHandler
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.github.felipecastilhos.pokedexandroid.features.home.data.datasource

import com.apollographql.apollo.ApolloClient
import com.apollographql.apollo.coroutines.await
import com.github.felipecastilhos.pokedexandroid.GetPokemonQuery
import com.github.felipecastilhos.pokedexandroid.core.datasource.Resource
import com.github.felipecastilhos.pokedexandroid.core.datasource.remote.RemoteDataSource
import com.github.felipecastilhos.pokedexandroid.core.network.toResource
import com.github.felipecastilhos.pokedexandroid.features.home.domain.models.Pokemon
import javax.inject.Inject

/**
* [PokemonDataSource] is an interface for all remote data source queries
*/
abstract class PokemonDataSource : RemoteDataSource {
abstract suspend fun search(): Resource<Pokemon?>
}

/**
* Implementation of [PokemonDataSource] searching in a GraphQl API
*/
class PokemonGraphQlDataSource @Inject constructor(private val apolloClient: ApolloClient) :
PokemonDataSource() {
override suspend fun search(): Resource<Pokemon?> {
return apolloClient.query(GetPokemonQuery())?.await().toResource { it?.getPokemon?.mapToDomainModel() }
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.github.felipecastilhos.pokedexandroid.features.home.di

import com.apollographql.apollo.ApolloClient
import com.github.felipecastilhos.pokedexandroid.features.home.data.datasource.HomeRemoteDataSource
import com.github.felipecastilhos.pokedexandroid.features.home.data.datasource.HomeRemoteGraphQlDataSourceExecutor
import com.github.felipecastilhos.pokedexandroid.features.home.domain.repository.PokemonRemoteDataRepositoryExecutor
import com.github.felipecastilhos.pokedexandroid.features.home.data.datasource.PokemonDataSource
import com.github.felipecastilhos.pokedexandroid.features.home.data.datasource.PokemonGraphQlDataSource
import com.github.felipecastilhos.pokedexandroid.features.home.domain.repository.DefaultPokemonRemoteDataRepository
import com.github.felipecastilhos.pokedexandroid.features.home.domain.repository.PokemonRepository
import com.github.felipecastilhos.pokedexandroid.features.home.domain.usecase.PokemonUseCase
import dagger.Module
Expand All @@ -23,8 +23,8 @@ class HomeModule {
@Provides
fun providesHomeRemoteDataSourceExecutor(
apolloClient: ApolloClient
): HomeRemoteDataSource {
return HomeRemoteGraphQlDataSourceExecutor(apolloClient)
): PokemonDataSource {
return PokemonGraphQlDataSource(apolloClient)
}

/**
Expand All @@ -33,17 +33,17 @@ class HomeModule {
*/
@Provides
fun providesPokemonRepository(
homeRemoteGraphQlDataSourceExecutor: HomeRemoteGraphQlDataSourceExecutor
homeRemoteGraphQlDataSourceExecutor: PokemonGraphQlDataSource
): PokemonRepository {
return PokemonRemoteDataRepositoryExecutor(homeRemoteGraphQlDataSourceExecutor)
return DefaultPokemonRemoteDataRepository(homeRemoteGraphQlDataSourceExecutor)
}

/**
* Provides the [PokemonUseCase] for view model business logic
* @param pokemonRemoteDataRepository contains all pokemon related data
*/
@Provides
fun providesPokemonUseCase(pokemonRemoteDataRepository: PokemonRemoteDataRepositoryExecutor): PokemonUseCase {
fun providesPokemonUseCase(pokemonRemoteDataRepository: DefaultPokemonRemoteDataRepository): PokemonUseCase {
return PokemonUseCase(pokemonRemoteDataRepository)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ enum class PokemonTypes {
Fire,
Water,
Glass,
Eletric,
Electric,
Psychic,
Ice,
Dragon,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package com.github.felipecastilhos.pokedexandroid.features.home.domain.repository

import com.github.felipecastilhos.pokedexandroid.GetPokemonQuery
import com.github.felipecastilhos.pokedexandroid.core.datasource.Resource
import com.github.felipecastilhos.pokedexandroid.features.home.data.datasource.HomeRemoteDataSource
import com.github.felipecastilhos.pokedexandroid.features.home.data.datasource.PokemonDataSource
import com.github.felipecastilhos.pokedexandroid.features.home.domain.models.Pokemon
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject

/**
Expand All @@ -15,33 +12,20 @@ interface PokemonRepository {
/**
* Query for all data of a single pokemon
*/
suspend fun search(): Flow<Resource<Pokemon?>>
suspend fun search(): Resource<Pokemon?>
}

/**
* Remote pokemon data repository
* @param homeRemoteDataSource for query pokemon data
* @param pokemonDataSource for query pokemon data
*/
class PokemonRemoteDataRepositoryExecutor @Inject constructor(
private val homeRemoteDataSource: HomeRemoteDataSource
class DefaultPokemonRemoteDataRepository @Inject constructor(
private val pokemonDataSource: PokemonDataSource
) :
PokemonRepository {
/**
* Query for all data of a single pokemon
*/
override suspend fun search(): Flow<Resource<Pokemon?>> =
homeRemoteDataSource.search().mapToDomainFlow()
override suspend fun search() = pokemonDataSource.search()
}

/**
* Map Apollo Client GraphQl Result to domain abstraction flow
*/
fun Flow<Resource<GetPokemonQuery.GetPokemon?>>.mapToDomainFlow(): Flow<Resource<Pokemon?>> {
return map {
when (it) {
is Resource.Error -> it
Resource.Loading -> Resource.Loading
is Resource.Success -> Resource.Success(it.data?.mapToDomainModel())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class PokemonUseCase @Inject constructor(
/**
* Retrieve all pokemon data
*/
suspend fun search(): Flow<Resource<Pokemon?>> {
suspend fun search(): Resource<Pokemon?> {
return pokemonRepository.search()
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.felipecastilhos.pokedexandroid.features.home.domain.viewmodel

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.github.felipecastilhos.pokedexandroid.core.coroutines.DispatcherProvider
import com.github.felipecastilhos.pokedexandroid.core.datasource.Resource
Expand All @@ -21,11 +22,10 @@ import javax.inject.Inject
class PokedexHomeViewModel @Inject constructor(
private val pokemonUseCase: PokemonUseCase,
dispatcherProvider: DispatcherProvider
) :
CoroutineViewModel(dispatcherProvider) {
protected val _stateFlow: MutableStateFlow<Resource<Pokemon?>> by lazy {
) : ViewModel() {
private val _stateFlow: MutableStateFlow<Resource<Pokemon?>> by lazy {
MutableStateFlow<Resource<Pokemon?>>(Resource.Loading).apply {
launchInIoScope {
viewModelScope.launch(dispatcherProvider.io) {
searchPokemon()
}
}
Expand All @@ -35,15 +35,8 @@ class PokedexHomeViewModel @Inject constructor(
/**
* Query pokemon data
*/
suspend fun searchPokemon(): Flow<Resource<Pokemon?>> {
viewModelScope.launch {
LogHandler.d("Searching Dragonite")
pokemonUseCase.search().collect {
LogHandler.d("Dragonite located")
_stateFlow.emit(it)
}
}

return pokemonUseCase.search()
suspend fun searchPokemon() {
LogHandler.d("Searching Dragonite")
_stateFlow.emit(pokemonUseCase.search())
}
}
4 changes: 2 additions & 2 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<resources>
<string name="app_name">Pokédex Android</string>

<string name="server_unexpected_error">"Sem resposta do servidor</string>
<string name="server_no_response">"Ops, algo deu errado</string>
<string name="server_unexpected_error">Ops, algo deu errado</string>
<string name="server_no_response">Sem conexão com o servidor, tente novamente mais tarde</string>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.github.felipecastilhos.pokedexandroid
felipecastilhos marked this conversation as resolved.
Show resolved Hide resolved

import com.github.felipecastilhos.pokedexandroid.core.datasource.Resource
import com.github.felipecastilhos.pokedexandroid.core.datasource.remote.DataSourceError
import com.github.felipecastilhos.pokedexandroid.features.home.data.datasource.PokemonDataSource
import com.github.felipecastilhos.pokedexandroid.features.home.domain.models.Pokemon

class FakePokemonDataSource(private val pokemon: Pokemon) : PokemonDataSource() {
override suspend fun search(): Resource<Pokemon> {
return Resource.Success(pokemon)
}
}

class FakeFailingPokemonDataSource : PokemonDataSource() {
override suspend fun search(): Resource<Pokemon?> {
return Resource.Error(exception = DataSourceError.Unexpected(R.string.server_unexpected_error))
}
}