Skip to content

Commit

Permalink
Separate Character and Episode API Services (#66)
Browse files Browse the repository at this point in the history
  • Loading branch information
mohsenoid authored Jul 8, 2024
1 parent 026f7dd commit 8f87404
Show file tree
Hide file tree
Showing 32 changed files with 213 additions and 185 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ private val dataModule =
module {
single<EpisodeRepository> {
EpisodeRepositoryImpl(
apiService = get(),
episodeApiService = get(),
episodeDao = get(),
)
}

single<CharacterRepository> {
CharacterRepositoryImpl(
apiService = get(),
characterApiService = get(),
characterDao = get(),
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.mohsenoid.rickandmorty.data.characters

import com.mohsenoid.rickandmorty.data.characters.dao.CharacterDao
import com.mohsenoid.rickandmorty.data.characters.db.dao.CharacterDao
import com.mohsenoid.rickandmorty.data.characters.mapper.CharacterMapper.toCharacter
import com.mohsenoid.rickandmorty.data.characters.mapper.CharacterMapper.toCharacterEntity
import com.mohsenoid.rickandmorty.data.remote.ApiService
import com.mohsenoid.rickandmorty.data.remote.model.CharacterRemoteModel
import com.mohsenoid.rickandmorty.data.characters.remote.CharacterApiService
import com.mohsenoid.rickandmorty.data.characters.remote.model.CharacterRemoteModel
import com.mohsenoid.rickandmorty.domain.NoInternetConnectionException
import com.mohsenoid.rickandmorty.domain.characters.CharacterRepository
import com.mohsenoid.rickandmorty.domain.characters.model.Character
Expand All @@ -15,7 +15,7 @@ import java.net.UnknownHostException
import java.util.SortedMap

internal class CharacterRepositoryImpl(
private val apiService: ApiService,
private val characterApiService: CharacterApiService,
private val characterDao: CharacterDao,
) : CharacterRepository {
private val charactersCache: SortedMap<Int, Character> = sortedMapOf()
Expand Down Expand Up @@ -44,25 +44,26 @@ internal class CharacterRepositoryImpl(
return getCharactersFromCache(charactersIds)
}

private suspend fun getCharactersFromRemote(charactersIds: Set<Int>): Result<List<Character>> {
val missingCharactersIds = charactersIds - charactersCache.keys
val missingCharactersIdsString = missingCharactersIds.joinToString(",")

return try {
val response = apiService.getCharacters(missingCharactersIdsString)
val remoteCharacters: List<CharacterRemoteModel>? = response.body()
if (response.isSuccessful && remoteCharacters != null) {
handleSuccessfulRemoteResponse(remoteCharacters)
getCharactersFromCache(charactersIds)!! // All characters should be cached
} else {
Result.failure(Exception(response.message().ifEmpty { "Unknown Error" }))
private suspend fun getCharactersFromRemote(charactersIds: Set<Int>): Result<List<Character>> =
withContext(Dispatchers.IO) {
val missingCharactersIds = charactersIds - charactersCache.keys
val missingCharactersIdsString = missingCharactersIds.joinToString(",")

try {
val response = characterApiService.getCharacters(missingCharactersIdsString)
val remoteCharacters: List<CharacterRemoteModel>? = response.body()
if (response.isSuccessful && remoteCharacters != null) {
handleSuccessfulRemoteResponse(remoteCharacters)
getCharactersFromCache(charactersIds)!! // All characters should be cached
} else {
Result.failure(Exception(response.message().ifEmpty { "Unknown Error" }))
}
} catch (e: UnknownHostException) {
Result.failure(NoInternetConnectionException(e.message))
} catch (e: SocketTimeoutException) {
Result.failure(NoInternetConnectionException(e.message))
}
} catch (e: UnknownHostException) {
Result.failure(NoInternetConnectionException(e.message))
} catch (e: SocketTimeoutException) {
Result.failure(NoInternetConnectionException(e.message))
}
}

private suspend fun handleSuccessfulRemoteResponse(remoteCharacters: List<CharacterRemoteModel>) {
val charactersEntity = remoteCharacters.map { it.toCharacterEntity() }
Expand Down Expand Up @@ -94,25 +95,26 @@ internal class CharacterRepositoryImpl(
}
}

private suspend fun getCharacterFromRemote(characterId: Int): Result<Character> {
return try {
val response = apiService.getCharacter(characterId)
val remoteCharacter: CharacterRemoteModel? = response.body()
if (response.isSuccessful && remoteCharacter != null) {
val characterEntity = remoteCharacter.toCharacterEntity()
characterDao.insertCharacter(characterEntity)
val character = characterEntity.toCharacter()
charactersCache += character.id to character
Result.success(character)
} else {
Result.failure(Exception(response.message().ifEmpty { "Unknown Error" }))
private suspend fun getCharacterFromRemote(characterId: Int): Result<Character> =
withContext(Dispatchers.IO) {
try {
val response = characterApiService.getCharacter(characterId)
val remoteCharacter: CharacterRemoteModel? = response.body()
if (response.isSuccessful && remoteCharacter != null) {
val characterEntity = remoteCharacter.toCharacterEntity()
characterDao.insertCharacter(characterEntity)
val character = characterEntity.toCharacter()
charactersCache += character.id to character
Result.success(character)
} else {
Result.failure(Exception(response.message().ifEmpty { "Unknown Error" }))
}
} catch (e: UnknownHostException) {
Result.failure(NoInternetConnectionException(e.message))
} catch (e: SocketTimeoutException) {
Result.failure(NoInternetConnectionException(e.message))
}
} catch (e: UnknownHostException) {
Result.failure(NoInternetConnectionException(e.message))
} catch (e: SocketTimeoutException) {
Result.failure(NoInternetConnectionException(e.message))
}
}

override suspend fun updateCharacterStatus(
characterId: Int,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.mohsenoid.rickandmorty.data.characters.dao
package com.mohsenoid.rickandmorty.data.characters.db.dao

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.mohsenoid.rickandmorty.data.characters.entity.CharacterEntity
import com.mohsenoid.rickandmorty.data.characters.db.entity.CharacterEntity

@Dao
internal interface CharacterDao {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.mohsenoid.rickandmorty.data.characters.entity
package com.mohsenoid.rickandmorty.data.characters.db.entity

import androidx.room.ColumnInfo
import androidx.room.Entity
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.mohsenoid.rickandmorty.data.characters.mapper

import com.mohsenoid.rickandmorty.data.characters.entity.CharacterEntity
import com.mohsenoid.rickandmorty.data.remote.model.CharacterRemoteModel
import com.mohsenoid.rickandmorty.data.characters.db.entity.CharacterEntity
import com.mohsenoid.rickandmorty.data.characters.remote.model.CharacterRemoteModel
import com.mohsenoid.rickandmorty.domain.characters.model.Character

internal object CharacterMapper {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.mohsenoid.rickandmorty.data.characters.remote

import com.mohsenoid.rickandmorty.data.characters.remote.model.CharacterRemoteModel
import com.mohsenoid.rickandmorty.data.characters.remote.model.CharactersResponse
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Path

internal interface CharacterApiService {
@GET("character/{characterIds}")
suspend fun getCharacters(
@Path("characterIds") characterIds: String,
): Response<CharactersResponse>

@GET("character/{characterId}")
suspend fun getCharacter(
@Path("characterId") characterId: Int,
): Response<CharacterRemoteModel>
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.mohsenoid.rickandmorty.data.remote.model
package com.mohsenoid.rickandmorty.data.characters.remote.model

import com.google.gson.annotations.SerializedName

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package com.mohsenoid.rickandmorty.data.remote.model
package com.mohsenoid.rickandmorty.data.characters.remote.model

internal typealias CharactersResponse = List<CharacterRemoteModel>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.mohsenoid.rickandmorty.data.remote.model
package com.mohsenoid.rickandmorty.data.characters.remote.model

import com.google.gson.annotations.SerializedName

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.mohsenoid.rickandmorty.data.remote.model
package com.mohsenoid.rickandmorty.data.characters.remote.model

import com.google.gson.annotations.SerializedName

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package com.mohsenoid.rickandmorty.data.db

import androidx.room.Database
import androidx.room.RoomDatabase
import com.mohsenoid.rickandmorty.data.characters.dao.CharacterDao
import com.mohsenoid.rickandmorty.data.characters.entity.CharacterEntity
import com.mohsenoid.rickandmorty.data.episodes.dao.EpisodeDao
import com.mohsenoid.rickandmorty.data.episodes.entity.EpisodeEntity
import com.mohsenoid.rickandmorty.data.characters.db.dao.CharacterDao
import com.mohsenoid.rickandmorty.data.characters.db.entity.CharacterEntity
import com.mohsenoid.rickandmorty.data.episodes.db.dao.EpisodeDao
import com.mohsenoid.rickandmorty.data.episodes.db.entity.EpisodeEntity

@Database(
entities = [EpisodeEntity::class, CharacterEntity::class],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.mohsenoid.rickandmorty.data.episodes

import com.mohsenoid.rickandmorty.data.episodes.dao.EpisodeDao
import com.mohsenoid.rickandmorty.data.episodes.db.dao.EpisodeDao
import com.mohsenoid.rickandmorty.data.episodes.mapper.EpisodeMapper.toEpisode
import com.mohsenoid.rickandmorty.data.episodes.mapper.EpisodeMapper.toEpisodeEntity
import com.mohsenoid.rickandmorty.data.remote.ApiService
import com.mohsenoid.rickandmorty.data.remote.model.EpisodeRemoteModel
import com.mohsenoid.rickandmorty.data.episodes.remote.EpisodeApiService
import com.mohsenoid.rickandmorty.data.episodes.remote.model.EpisodeRemoteModel
import com.mohsenoid.rickandmorty.domain.EndOfListException
import com.mohsenoid.rickandmorty.domain.NoInternetConnectionException
import com.mohsenoid.rickandmorty.domain.episodes.EpisodeRepository
Expand All @@ -16,7 +16,7 @@ import java.net.SocketTimeoutException
import java.net.UnknownHostException

internal class EpisodeRepositoryImpl(
private val apiService: ApiService,
private val episodeApiService: EpisodeApiService,
private val episodeDao: EpisodeDao,
) : EpisodeRepository {
private val episodesCache: MutableMap<Int, List<Episode>> = mutableMapOf()
Expand All @@ -42,24 +42,25 @@ internal class EpisodeRepositoryImpl(
}
}

private suspend fun getEpisodesFromRemote(page: Int): Result<List<Episode>> {
return try {
val response = apiService.getEpisodes(page)
val remoteEpisodes = response.body()?.results
if (response.isSuccessful && remoteEpisodes != null) {
handleSuccessfulRemoteResponse(page, remoteEpisodes)
getEpisodesFromCache(page)!! // All episodes should be cached
} else if (response.code() == HttpURLConnection.HTTP_NOT_FOUND && page != 0) {
Result.failure(EndOfListException())
} else {
Result.failure(Exception(response.message().ifEmpty { "Unknown Error" }))
private suspend fun getEpisodesFromRemote(page: Int): Result<List<Episode>> =
withContext(Dispatchers.IO) {
try {
val response = episodeApiService.getEpisodes(page)
val remoteEpisodes = response.body()?.results
if (response.isSuccessful && remoteEpisodes != null) {
handleSuccessfulRemoteResponse(page, remoteEpisodes)
getEpisodesFromCache(page)!! // All episodes should be cached
} else if (response.code() == HttpURLConnection.HTTP_NOT_FOUND && page != 0) {
Result.failure(EndOfListException())
} else {
Result.failure(Exception(response.message().ifEmpty { "Unknown Error" }))
}
} catch (e: UnknownHostException) {
Result.failure(NoInternetConnectionException(e.message))
} catch (e: SocketTimeoutException) {
Result.failure(NoInternetConnectionException(e.message))
}
} catch (e: UnknownHostException) {
Result.failure(NoInternetConnectionException(e.message))
} catch (e: SocketTimeoutException) {
Result.failure(NoInternetConnectionException(e.message))
}
}

private suspend fun handleSuccessfulRemoteResponse(
page: Int,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.mohsenoid.rickandmorty.data.episodes.dao
package com.mohsenoid.rickandmorty.data.episodes.db.dao

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.mohsenoid.rickandmorty.data.episodes.entity.EpisodeEntity
import com.mohsenoid.rickandmorty.data.episodes.db.entity.EpisodeEntity

@Dao
internal interface EpisodeDao {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.mohsenoid.rickandmorty.data.episodes.entity
package com.mohsenoid.rickandmorty.data.episodes.db.entity

import androidx.room.ColumnInfo
import androidx.room.Entity
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.mohsenoid.rickandmorty.data.episodes.mapper

import com.mohsenoid.rickandmorty.data.episodes.entity.EpisodeEntity
import com.mohsenoid.rickandmorty.data.remote.model.EpisodeRemoteModel
import com.mohsenoid.rickandmorty.data.episodes.db.entity.EpisodeEntity
import com.mohsenoid.rickandmorty.data.episodes.remote.model.EpisodeRemoteModel
import com.mohsenoid.rickandmorty.domain.episodes.model.Episode

internal object EpisodeMapper {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.mohsenoid.rickandmorty.data.episodes.remote

import com.mohsenoid.rickandmorty.data.episodes.remote.model.EpisodesResponse
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Query

internal interface EpisodeApiService {
@GET("episode")
suspend fun getEpisodes(
@Query("page") page: Int,
): Response<EpisodesResponse>
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.mohsenoid.rickandmorty.data.remote.model
package com.mohsenoid.rickandmorty.data.episodes.remote.model

import com.google.gson.annotations.SerializedName

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.mohsenoid.rickandmorty.data.remote.model
package com.mohsenoid.rickandmorty.data.episodes.remote.model

import com.google.gson.annotations.SerializedName

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.mohsenoid.rickandmorty.data.remote.model
package com.mohsenoid.rickandmorty.data.episodes.remote.model

import com.google.gson.annotations.SerializedName

Expand Down

This file was deleted.

Loading

0 comments on commit 8f87404

Please sign in to comment.