diff --git a/app/src/main/java/ru/fincode/tsudesk/MainActivity.kt b/app/src/main/java/ru/fincode/tsudesk/MainActivity.kt index 19e05d2..11c7979 100644 --- a/app/src/main/java/ru/fincode/tsudesk/MainActivity.kt +++ b/app/src/main/java/ru/fincode/tsudesk/MainActivity.kt @@ -23,7 +23,7 @@ class MainActivity : ComponentActivity() { val result = getScheduleUseCase( ScheduleType.Group("220631") ) - Log.d("TSUDesk", result.isSuccess.toString()) + Log.d("TSUDesk", result.toString()) } catch (e: Exception) { Log.e("TSUDesk", "Error loading schedule", e) } diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts index 1fd1ccf..acfaf8b 100644 --- a/core/network/build.gradle.kts +++ b/core/network/build.gradle.kts @@ -48,6 +48,4 @@ dependencies { api(libs.moshi) api(libs.moshiKotlin) api(libs.retrofitMoshi) - - api(libs.retrofit.simplexml) } \ No newline at end of file diff --git a/core/network/src/main/java/ru/fincode/tsudesk/core/network/NetworkCall.kt b/core/network/src/main/java/ru/fincode/tsudesk/core/network/NetworkCall.kt index 0c61054..d249f44 100644 --- a/core/network/src/main/java/ru/fincode/tsudesk/core/network/NetworkCall.kt +++ b/core/network/src/main/java/ru/fincode/tsudesk/core/network/NetworkCall.kt @@ -1,4 +1,24 @@ package ru.fincode.tsudesk.core.network -class NetworkCall { -} \ No newline at end of file +import retrofit2.HttpException +import ru.fincode.tsudesk.core.network.model.NetworkError +import ru.fincode.tsudesk.core.network.model.NetworkResult +import java.io.IOException +import java.net.SocketTimeoutException + +suspend inline fun safeApiCall( + crossinline block: suspend () -> T +): NetworkResult { + return try { + NetworkResult.Success(block()) + } catch (t: Throwable) { + NetworkResult.Error(t.toNetworkError()) + } +} + +fun Throwable.toNetworkError(): NetworkError = when (this) { + is SocketTimeoutException -> NetworkError.Timeout + is IOException -> NetworkError.NoInternet + is HttpException -> NetworkError.Http(code()) + else -> NetworkError.Unknown(this) +} diff --git a/core/network/src/main/java/ru/fincode/tsudesk/core/network/NetworkModule.kt b/core/network/src/main/java/ru/fincode/tsudesk/core/network/NetworkModule.kt index 45dc8d8..d3ad925 100644 --- a/core/network/src/main/java/ru/fincode/tsudesk/core/network/NetworkModule.kt +++ b/core/network/src/main/java/ru/fincode/tsudesk/core/network/NetworkModule.kt @@ -9,12 +9,13 @@ import retrofit2.Retrofit import java.util.concurrent.TimeUnit import javax.inject.Singleton import okhttp3.logging.HttpLoggingInterceptor +import ru.fincode.tsudesk.core.network.interceptor.DebugInterceptor @Module @InstallIn(SingletonComponent::class) object NetworkModule { - private const val TIMEOUT_SEC = 30L + private const val TIMEOUT = 30L val logging = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY } @@ -23,9 +24,9 @@ object NetworkModule { @Singleton fun provideOkHttpClient(): OkHttpClient = OkHttpClient.Builder() - .connectTimeout(TIMEOUT_SEC, TimeUnit.SECONDS) - .readTimeout(TIMEOUT_SEC, TimeUnit.SECONDS) - .writeTimeout(TIMEOUT_SEC, TimeUnit.SECONDS) + .connectTimeout(TIMEOUT, TimeUnit.SECONDS) + .readTimeout(TIMEOUT, TimeUnit.SECONDS) + .writeTimeout(TIMEOUT, TimeUnit.SECONDS) .retryOnConnectionFailure(true) .addInterceptor(DebugInterceptor()) .build() diff --git a/core/network/src/main/java/ru/fincode/tsudesk/core/network/interceptor/DebugInterceptor.kt b/core/network/src/main/java/ru/fincode/tsudesk/core/network/interceptor/DebugInterceptor.kt index 323d595..f360378 100644 --- a/core/network/src/main/java/ru/fincode/tsudesk/core/network/interceptor/DebugInterceptor.kt +++ b/core/network/src/main/java/ru/fincode/tsudesk/core/network/interceptor/DebugInterceptor.kt @@ -1,2 +1,20 @@ -package ru.fincode.tsudesk.core.network.interceptor +package ru.fincode.tsudesk.core.network.interceptor +import android.util.Log +import okhttp3.Interceptor +import okhttp3.Response + +private const val TAG = "NETWORK_DEBUG" + +class DebugInterceptor : Interceptor { + + override fun intercept(chain: Interceptor.Chain): Response { + val request = chain.request() + + Log.d(TAG, "URL: ${request.url}") + Log.d(TAG, "Method: ${request.method}") + Log.d(TAG, "Headers: ${request.headers}") + + return chain.proceed(request) + } +} \ No newline at end of file diff --git a/core/network/src/main/java/ru/fincode/tsudesk/core/network/model/NetworkError.kt b/core/network/src/main/java/ru/fincode/tsudesk/core/network/model/NetworkError.kt index 0cb6b6a..5aead0a 100644 --- a/core/network/src/main/java/ru/fincode/tsudesk/core/network/model/NetworkError.kt +++ b/core/network/src/main/java/ru/fincode/tsudesk/core/network/model/NetworkError.kt @@ -1,2 +1,8 @@ -package ru.fincode.tsudesk.core.network.model +package ru.fincode.tsudesk.core.network.model +sealed class NetworkError { + object NoInternet : NetworkError() + object Timeout : NetworkError() + data class Http(val code: Int) : NetworkError() + data class Unknown(val throwable: Throwable) : NetworkError() +} \ No newline at end of file diff --git a/core/network/src/main/java/ru/fincode/tsudesk/core/network/model/NetworkResult.kt b/core/network/src/main/java/ru/fincode/tsudesk/core/network/model/NetworkResult.kt index 0cb6b6a..7e77fbf 100644 --- a/core/network/src/main/java/ru/fincode/tsudesk/core/network/model/NetworkResult.kt +++ b/core/network/src/main/java/ru/fincode/tsudesk/core/network/model/NetworkResult.kt @@ -1,2 +1,6 @@ -package ru.fincode.tsudesk.core.network.model +package ru.fincode.tsudesk.core.network.model +sealed class NetworkResult { + data class Success(val data: T) : NetworkResult() + data class Error(val error: NetworkError) : NetworkResult() +} \ No newline at end of file diff --git a/core/network/src/main/java/ru/fincode/tsudesk/core/network/model/NetworkResultExt.kt b/core/network/src/main/java/ru/fincode/tsudesk/core/network/model/NetworkResultExt.kt index 561c850..69d4e04 100644 --- a/core/network/src/main/java/ru/fincode/tsudesk/core/network/model/NetworkResultExt.kt +++ b/core/network/src/main/java/ru/fincode/tsudesk/core/network/model/NetworkResultExt.kt @@ -1,4 +1,8 @@ package ru.fincode.tsudesk.core.network.model -class NetworkResultExt { -} \ No newline at end of file +inline fun NetworkResult.map( + transform: (T) -> R +): NetworkResult = when (this) { + is NetworkResult.Success -> NetworkResult.Success(transform(data)) + is NetworkResult.Error -> this +} diff --git a/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/data/ScheduleRepositoryImpl.kt b/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/data/ScheduleRepositoryImpl.kt index 2f58e82..8fcf4ec 100644 --- a/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/data/ScheduleRepositoryImpl.kt +++ b/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/data/ScheduleRepositoryImpl.kt @@ -1,5 +1,7 @@ package ru.fincode.tsudesk.feature.schedule.data +import ru.fincode.tsudesk.core.network.model.NetworkResult +import ru.fincode.tsudesk.core.network.model.map import ru.fincode.tsudesk.feature.schedule.data.datasource.ScheduleRemoteDataSource import ru.fincode.tsudesk.feature.schedule.data.mapper.ScheduleDtoToDomainMapper import ru.fincode.tsudesk.feature.schedule.domain.model.ScheduleEntity @@ -7,14 +9,15 @@ import ru.fincode.tsudesk.feature.schedule.domain.repository.ScheduleRepository import javax.inject.Inject class ScheduleRepositoryImpl @Inject constructor( - private val remote: ScheduleRemoteDataSource, private val mapper: ScheduleDtoToDomainMapper + private val remote: ScheduleRemoteDataSource, + private val mapper: ScheduleDtoToDomainMapper ) : ScheduleRepository { - override suspend fun loadScheduleByGroup( - groupNumber: String - ): Result = remote.loadScheduleByGroup(groupNumber).map(mapper::invoke) + override suspend fun loadScheduleByGroup(number: String): NetworkResult = + remote.loadScheduleByGroup(number).map(mapper::invoke) + + override suspend fun loadScheduleByTeacher(name: String): NetworkResult = + remote.loadScheduleByTeacher(name).map(mapper::invoke) - override suspend fun loadScheduleByTeacher( - name: String - ): Result = remote.loadScheduleByTeacher(name).map(mapper::invoke) } + diff --git a/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/data/datasource/ScheduleRemoteDataSource.kt b/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/data/datasource/ScheduleRemoteDataSource.kt index 728ac79..a90ecda 100644 --- a/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/data/datasource/ScheduleRemoteDataSource.kt +++ b/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/data/datasource/ScheduleRemoteDataSource.kt @@ -1,5 +1,7 @@ package ru.fincode.tsudesk.feature.schedule.data.datasource +import ru.fincode.tsudesk.core.network.model.NetworkResult +import ru.fincode.tsudesk.core.network.safeApiCall import ru.fincode.tsudesk.feature.schedule.data.remote.ScheduleApi import ru.fincode.tsudesk.feature.schedule.data.remote.model.LessonDto.ScheduleSearchField.GROUP_P import ru.fincode.tsudesk.feature.schedule.data.remote.model.LessonDto.ScheduleSearchField.PREP @@ -9,9 +11,9 @@ import javax.inject.Inject class ScheduleRemoteDataSource @Inject constructor( private val api: ScheduleApi ) { - suspend fun loadScheduleByGroup(groupNumber: String): Result = - runCatching { api.getSchedule(GROUP_P, groupNumber) } + suspend fun loadScheduleByGroup(number: String): NetworkResult = + safeApiCall { api.getSchedule(GROUP_P, number) } - suspend fun loadScheduleByTeacher(name: String): Result = - runCatching { api.getSchedule(PREP, name) } + suspend fun loadScheduleByTeacher(name: String): NetworkResult = + safeApiCall { api.getSchedule(PREP, name) } } diff --git a/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/data/datasource/ScheduleRemoteDataSourceImpl.kt b/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/data/datasource/ScheduleRemoteDataSourceImpl.kt index e7b8e0e..2d56f2f 100644 --- a/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/data/datasource/ScheduleRemoteDataSourceImpl.kt +++ b/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/data/datasource/ScheduleRemoteDataSourceImpl.kt @@ -1,5 +1,7 @@ package ru.fincode.tsudesk.feature.schedule.data.datasource +import ru.fincode.tsudesk.core.network.model.NetworkResult +import ru.fincode.tsudesk.core.network.model.map import ru.fincode.tsudesk.feature.schedule.data.mapper.ScheduleDtoToDomainMapper import ru.fincode.tsudesk.feature.schedule.domain.model.ScheduleEntity import ru.fincode.tsudesk.feature.schedule.domain.repository.ScheduleRepository @@ -10,13 +12,10 @@ class ScheduleRemoteDataSourceImpl @Inject constructor( private val mapper: ScheduleDtoToDomainMapper ) : ScheduleRepository { - override suspend fun loadScheduleByGroup(groupNumber: String): Result = - remote - .loadScheduleByGroup(groupNumber) // Result - .map(mapper::invoke) // Result + override suspend fun loadScheduleByGroup(number: String): NetworkResult = + remote.loadScheduleByGroup(number).map(mapper::invoke) + + override suspend fun loadScheduleByTeacher(name: String): NetworkResult = + remote.loadScheduleByTeacher(name).map(mapper::invoke) - override suspend fun loadScheduleByTeacher(name: String): Result = - remote - .loadScheduleByTeacher(name) // Result - .map(mapper::invoke) // Result } diff --git a/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/domain/repository/ScheduleRepository.kt b/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/domain/repository/ScheduleRepository.kt index 6b6746a..a85bf5a 100644 --- a/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/domain/repository/ScheduleRepository.kt +++ b/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/domain/repository/ScheduleRepository.kt @@ -1,8 +1,9 @@ package ru.fincode.tsudesk.feature.schedule.domain.repository +import ru.fincode.tsudesk.core.network.model.NetworkResult import ru.fincode.tsudesk.feature.schedule.domain.model.ScheduleEntity interface ScheduleRepository { - suspend fun loadScheduleByGroup(groupNumber: String): Result - suspend fun loadScheduleByTeacher(name: String): Result + suspend fun loadScheduleByGroup(number: String): NetworkResult + suspend fun loadScheduleByTeacher(name: String): NetworkResult } diff --git a/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/domain/usecase/GetScheduleUseCase.kt b/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/domain/usecase/GetScheduleUseCase.kt index 84068fb..acb89e8 100644 --- a/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/domain/usecase/GetScheduleUseCase.kt +++ b/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/domain/usecase/GetScheduleUseCase.kt @@ -1,5 +1,6 @@ package ru.fincode.tsudesk.feature.schedule.domain.usecase +import ru.fincode.tsudesk.core.network.model.NetworkResult import ru.fincode.tsudesk.feature.schedule.domain.model.ScheduleEntity import ru.fincode.tsudesk.feature.schedule.domain.repository.ScheduleRepository import javax.inject.Inject @@ -8,13 +9,13 @@ class GetScheduleUseCase @Inject constructor( private val repository: ScheduleRepository ) { sealed interface ScheduleType { - data class Group(val groupNumber: String) : ScheduleType - data class Teacher(val teacherName: String) : ScheduleType + data class Group(val number: String) : ScheduleType + data class Teacher(val name: String) : ScheduleType } - suspend operator fun invoke(type: ScheduleType): Result = + suspend operator fun invoke(type: ScheduleType): NetworkResult = when (type) { - is ScheduleType.Group -> repository.loadScheduleByGroup(type.groupNumber) - is ScheduleType.Teacher -> repository.loadScheduleByTeacher(type.teacherName) + is ScheduleType.Group -> repository.loadScheduleByGroup(type.number) + is ScheduleType.Teacher -> repository.loadScheduleByTeacher(type.name) } }