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

Open Table challenge #10

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ android {
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

buildConfigField(
"String",
"apiKey",
"\"${project.findProperty("apiKey")}\""
)

buildFeatures {
buildConfig = true
}
}

buildTypes {
Expand Down Expand Up @@ -44,6 +54,10 @@ dependencies {
implementation libs.dagger
kapt libs.dagger.compiler

implementation libs.converter.gson
implementation libs.coroutines.android
implementation libs.coroutines.retrofit.adapter

//retrofit
implementation libs.retrofit
implementation libs.retrofit.rx.adapter
Expand All @@ -57,6 +71,7 @@ dependencies {
implementation libs.rx.kotlin

testImplementation libs.junit
testImplementation libs.coroutines.test
androidTestImplementation libs.androidx.junit
androidTestImplementation libs.androidx.espresso.core
}
4 changes: 3 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".MyApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
Expand All @@ -13,7 +15,7 @@
android:theme="@style/Theme.AndroidOTChallenge"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:name=".presentation.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down
22 changes: 0 additions & 22 deletions app/src/main/java/com/example/otchallenge/MainActivity.kt

This file was deleted.

4 changes: 1 addition & 3 deletions app/src/main/java/com/example/otchallenge/MyApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ package com.example.otchallenge
import android.app.Application
import com.example.otchallenge.di.AppComponent
import com.example.otchallenge.di.DaggerAppComponent
import com.example.otchallenge.di.PresentationModule

class MyApplication : Application() {

lateinit var appComponent: AppComponent

override fun onCreate() {
super.onCreate()
appComponent = DaggerAppComponent.builder().build()
}
}
38 changes: 38 additions & 0 deletions app/src/main/java/com/example/otchallenge/data/BooksRepoImpl.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.example.otchallenge.data

import com.example.otchallenge.data.local.BookDao
import com.example.otchallenge.data.remote.BooksApi
import com.example.otchallenge.data.remote.ToBookEntity
import com.example.otchallenge.domain.BookDomain
import com.example.otchallenge.domain.BooksRepo
import com.example.otchallenge.domain.RepoResponse
import com.example.otchallenge.domain.toDomain

class BooksRepoImpl(
private val booksApi: BooksApi,
private val bookDao: BookDao,
) : BooksRepo{

override suspend fun getAllBooks(): RepoResponse<List<BookDomain>> {
return try {
val response = booksApi.getAllBooks()
if (response.isSuccessful) {
val results = response.body()!!.results.books.map { it.ToBookEntity() }
bookDao.saveAll(results)
val storedResults = getResultsFromDao()
RepoResponse.Success(storedResults)
} else {
val storedResults = getResultsFromDao()
RepoResponse.Error(response.message(), storedResults)
}
} catch (ex: Exception) {
//todo In a prod app I would map the error to a message, but keeping this simple
val storedResults = getResultsFromDao()
RepoResponse.Error(ex.message.orEmpty(), storedResults)
}
}

private suspend fun getResultsFromDao() = bookDao.getAll().map { it.toDomain() }


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.otchallenge.data.local

interface BookDao {

suspend fun saveAll(books: List<BookEntity>)
suspend fun getAll() : List<BookEntity>
}
10 changes: 10 additions & 0 deletions app/src/main/java/com/example/otchallenge/data/local/BookEntity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example.otchallenge.data.local

import com.google.gson.annotations.SerializedName

data class BookEntity(
val title: String,
val description: String,
val image: String,
)

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.example.otchallenge.data.local

//In a production app this would be a Room database
class InMemoryDao: BookDao {

private var books: List<BookEntity> = emptyList()

override suspend fun saveAll(books: List<BookEntity>) {
this.books = books
}

override suspend fun getAll(): List<BookEntity> {
return books
}
}
29 changes: 29 additions & 0 deletions app/src/main/java/com/example/otchallenge/data/remote/BookDto.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.example.otchallenge.data.remote

import com.example.otchallenge.data.local.BookEntity
import com.google.gson.annotations.SerializedName

data class ResponseDto(
@SerializedName("results")
val results: ResultsDto
)

data class ResultsDto(
@SerializedName("books")
val books: List<BookDto>
)

data class BookDto(
@SerializedName("title")
val title: String,
@SerializedName("description")
val description: String,
@SerializedName("book_image")
val image: String
)

fun BookDto.ToBookEntity() = BookEntity(
this.title,
this.description,
this.image
)
17 changes: 17 additions & 0 deletions app/src/main/java/com/example/otchallenge/data/remote/BooksApi.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.example.otchallenge.data.remote

import com.example.otchallenge.BuildConfig
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Query

interface BooksApi {


@GET("svc/books/v3/lists/current/hardcover-fiction.json")
suspend fun getAllBooks(
@Query("api-key") apikey: String = BuildConfig.apiKey,
@Query("offset") offset: Int =0,
): Response<ResponseDto>

}
6 changes: 3 additions & 3 deletions app/src/main/java/com/example/otchallenge/di/AppComponent.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.example.otchallenge.di

import com.example.otchallenge.MainActivity
import com.example.otchallenge.presentation.MainActivity
import dagger.Component
import javax.inject.Singleton

@Singleton
@Component
@Component(modules = [PresentationModule::class, DataModule::class, DomainModule::class])
interface AppComponent {
fun inject(activity: MainActivity)
fun inject(activity: MainActivity)
}
31 changes: 31 additions & 0 deletions app/src/main/java/com/example/otchallenge/di/DataModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.example.otchallenge.di

import com.example.otchallenge.data.local.BookDao
import com.example.otchallenge.data.local.InMemoryDao
import com.example.otchallenge.data.remote.BooksApi
import dagger.Binds
import dagger.Module
import dagger.Provides
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import javax.inject.Singleton

@Module
interface DataModule {

@Binds
fun providesBookDao(inMemoryDao: InMemoryDao): BookDao

companion object {
@Provides
fun provideStockAPI(): BooksApi =
Retrofit.Builder().baseUrl("https://api.nytimes.com")
.addConverterFactory(GsonConverterFactory.create()).build()
.create(BooksApi::class.java)

@Provides
fun providesInMemoryDao() = InMemoryDao()
}


}
22 changes: 22 additions & 0 deletions app/src/main/java/com/example/otchallenge/di/DomainModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.example.otchallenge.di

import com.example.otchallenge.data.BooksRepoImpl
import com.example.otchallenge.data.local.BookDao
import com.example.otchallenge.data.remote.BooksApi
import com.example.otchallenge.domain.BooksRepo
import dagger.Binds
import dagger.Module
import dagger.Provides

@Module
interface DomainModule {

@Binds
fun bindsBooksRepo(booksRepoImpl: BooksRepoImpl): BooksRepo

companion object {
@Provides
fun providesBooksRepoImpl(booksApi: BooksApi, bookDao: BookDao) =
BooksRepoImpl(booksApi, bookDao)
}
}
24 changes: 24 additions & 0 deletions app/src/main/java/com/example/otchallenge/di/PresentationModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.example.otchallenge.di

import com.example.otchallenge.domain.BooksRepo
import com.example.otchallenge.presentation.OtpContract
import com.example.otchallenge.presentation.OtpPresenter
import dagger.Binds
import dagger.Module
import dagger.Provides

@Module(includes = [PresentationModule.Bindings::class])
class PresentationModule(private val activity: OtpContract.View) {

@Provides
fun providesPresenter(booksRepo: BooksRepo) = OtpPresenter(activity, booksRepo)

@Module
interface Bindings {

@Binds
fun bindsOtpPresenter(otpPresenter: OtpPresenter): OtpContract.Presenter

}

}
15 changes: 15 additions & 0 deletions app/src/main/java/com/example/otchallenge/domain/BookDomain.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.example.otchallenge.domain

import com.example.otchallenge.data.local.BookEntity

data class BookDomain(
val title: String,
val description: String,
val url: String,
)

fun BookEntity.toDomain() = BookDomain(
this.title,
this.description,
this.image
)
6 changes: 6 additions & 0 deletions app/src/main/java/com/example/otchallenge/domain/BooksRepo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.example.otchallenge.domain

interface BooksRepo {

suspend fun getAllBooks(): RepoResponse<List<BookDomain>>
}
11 changes: 11 additions & 0 deletions app/src/main/java/com/example/otchallenge/domain/RepoResponse.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.otchallenge.domain



sealed class RepoResponse<T> {

data class Success<T>(val data: T) : RepoResponse<T>()

data class Error<T>(val error: String, val data: T) : RepoResponse<T>()

}
Loading