mirror of
https://github.com/AdrianKuta/android-challange-adrian-kuta.git
synced 2025-07-02 05:37:59 +02:00
feat: Implement airport data fetching and display
This commit introduces the functionality to fetch airport data from the Ryanair API and display it in the UI. Key changes: - Added `AirportInfoModel` interface and its implementation `AirportInfoModelImpl` to represent airport data in the data layer. - Created `AirportInfoModelMapper` to map `AirportResponse` to `AirportInfoModel`. - Set the base URL for Retrofit in `NetworkModule` to `https://services-api.ryanair.com`. - Added new Gradle modules: `model:data:shared`, `model:datasource:airports`, `model:datasource:shared`, and `domain:search`. - Implemented `CacheImpl` in `model:data:shared` for generic caching. - Defined `ObserveAirportsUseCase` interface in `domain:search` to observe airport data. - Added Detekt configuration for the `domain:search` and `model:datasource:shared` modules. - Created `AirportsDatasource` interface and its implementation `AirportsDatasourceImpl` to manage airport data. - Implemented `AirportInfoDomainMapper` to map `AirportInfoModel` to the domain `AirportInfo`. - Updated `HomeScreen` to display a list of airports using `LazyColumn`. - Updated `HomeScreenViewModel` to fetch and expose airport data via `ObserveAirportsUseCase`. - Added `ObserveAirportsUseCaseModule` to provide the `ObserveAirportsUseCase` implementation. - Implemented `ObserveAirportsUseCaseImpl` in the repository layer to fetch data from the API and update the datasource. - Added `kotlinx-datetime` dependency. - Created `AirportsDatasourceModule` to provide `AirportsDatasource` and its implementation. - Added `CacheObservers.kt` with a utility function `loadData` to handle cache observation and data loading logic. - Updated dependencies in `model:data:simple`, `model:repository`, and `ui:home` build files. - Updated `settings.gradle.kts` to include new modules. - Defined `Cache` interface in `model:datasource:shared`.
This commit is contained in:
@ -39,6 +39,7 @@ class NetworkModule {
|
||||
@Provides
|
||||
fun provideRetrofit(client: OkHttpClient, moshi: Moshi): Retrofit = Retrofit.Builder()
|
||||
// TODO("add URL provided in the task instructions")
|
||||
.baseUrl("https://services-api.ryanair.com")
|
||||
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||
.client(client)
|
||||
.build()
|
||||
|
14
model/data/shared/build.gradle.kts
Normal file
14
model/data/shared/build.gradle.kts
Normal file
@ -0,0 +1,14 @@
|
||||
plugins {
|
||||
alias(libs.plugins.flights.android.library)
|
||||
alias(libs.plugins.flights.android.library.hilt)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "dev.adriankuta.flights.model.data.shared"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.model.datasource.shared)
|
||||
|
||||
implementation(libs.timber)
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package dev.adriankuta.flights.model.data.shared
|
||||
|
||||
import dev.adriankuta.flights.model.datasource.shared.Cache
|
||||
|
||||
public data class CacheImpl<T>(override val cacheKey: String?, override val data: T?) :
|
||||
Cache<T> {
|
||||
public companion object {
|
||||
public inline fun <reified T> emptyCache(): CacheImpl<T> =
|
||||
CacheImpl(null, null)
|
||||
|
||||
public inline fun <reified T> Cache<T>?.orEmpty(): Cache<T> =
|
||||
this ?: emptyCache()
|
||||
}
|
||||
}
|
@ -9,6 +9,9 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation(projects.core.util)
|
||||
implementation(projects.model.data.api)
|
||||
implementation(projects.model.data.shared)
|
||||
implementation(projects.model.datasource.airports)
|
||||
|
||||
implementation(libs.timber)
|
||||
implementation(libs.gson)
|
||||
|
@ -0,0 +1,28 @@
|
||||
package dev.adriankuta.flights.model.data.simple
|
||||
|
||||
import dev.adriankuta.flights.model.data.shared.CacheImpl
|
||||
import dev.adriankuta.flights.model.data.simple.mappers.toModel
|
||||
import dev.adriankuta.flights.model.datasource.airports.AirportsDatasource
|
||||
import dev.adriankuta.flights.model.datasource.airports.entities.AirportInfoModel
|
||||
import dev.adriankuta.flights.model.datasource.shared.Cache
|
||||
import dev.adriankuta.model.data.api.entities.AirportResponse
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
internal class AirportsDatasourceImpl : AirportsDatasource {
|
||||
|
||||
private val _airports = MutableStateFlow(CacheImpl.emptyCache<List<AirportInfoModel>>())
|
||||
|
||||
override val airports: StateFlow<Cache<List<AirportInfoModel>>> = _airports.asStateFlow()
|
||||
|
||||
override suspend fun setAirportsInfo(
|
||||
airports: List<AirportResponse>,
|
||||
cacheKey: String
|
||||
) {
|
||||
_airports.value = CacheImpl(
|
||||
cacheKey = cacheKey,
|
||||
data = airports.map { it.toModel() }
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package dev.adriankuta.flights.model.data.simple.di
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dev.adriankuta.flights.model.data.simple.AirportsDatasourceImpl
|
||||
import dev.adriankuta.flights.model.datasource.airports.AirportsDatasource
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
@Suppress("UnnecessaryAbstractClass")
|
||||
internal abstract class AirportsDatasourceModule {
|
||||
@Binds
|
||||
abstract fun bindApi(impl: AirportsDatasourceImpl): AirportsDatasource
|
||||
|
||||
/*@Binds
|
||||
@IntoSet
|
||||
abstract fun bindReset(impl: RolesDatasourceImpl): ClearDatasource*/
|
||||
}
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal class AirportsDatasourceImplModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provide(): AirportsDatasourceImpl = AirportsDatasourceImpl()
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package dev.adriankuta.flights.model.data.simple.mappers
|
||||
|
||||
import dev.adriankuta.flights.model.datasource.airports.entities.AirportInfoModel
|
||||
import dev.adriankuta.model.data.api.entities.AirportResponse
|
||||
|
||||
internal fun AirportResponse.toModel(): AirportInfoModel {
|
||||
return AirportInfoModelImpl(
|
||||
code = code.orEmpty(),
|
||||
name = name.orEmpty(),
|
||||
seoName = seoName.orEmpty(),
|
||||
isBase = isBase ?: false,
|
||||
timeZone = timeZone.orEmpty(),
|
||||
cityCode = city?.code.orEmpty(),
|
||||
cityName = city?.name.orEmpty(),
|
||||
macCode = macCity?.macCode.orEmpty(),
|
||||
regionCode = region?.code.orEmpty(),
|
||||
regionName = region?.name.orEmpty(),
|
||||
countryCode = country?.code.orEmpty(),
|
||||
countryName = country?.name.orEmpty(),
|
||||
countryCurrencyCode = country?.currencyCode.orEmpty(),
|
||||
latitude = coordinates?.latitude ?: 0.0,
|
||||
longitude = coordinates?.longitude ?: 0.0
|
||||
)
|
||||
}
|
||||
|
||||
private data class AirportInfoModelImpl(
|
||||
override val code: String,
|
||||
override val name: String,
|
||||
override val seoName: String,
|
||||
override val isBase: Boolean,
|
||||
override val timeZone: String,
|
||||
override val cityCode: String,
|
||||
override val cityName: String,
|
||||
override val macCode: String,
|
||||
override val regionCode: String,
|
||||
override val regionName: String,
|
||||
override val countryCode: String,
|
||||
override val countryName: String,
|
||||
override val countryCurrencyCode: String,
|
||||
override val latitude: Double,
|
||||
override val longitude: Double
|
||||
) : AirportInfoModel
|
Reference in New Issue
Block a user