diff --git a/backend/src/main/kotlin/me/sujanpoudel/playdeals/repositories/DealRepository.kt b/backend/src/main/kotlin/me/sujanpoudel/playdeals/repositories/DealRepository.kt index 587a9c1..979bb3c 100644 --- a/backend/src/main/kotlin/me/sujanpoudel/playdeals/repositories/DealRepository.kt +++ b/backend/src/main/kotlin/me/sujanpoudel/playdeals/repositories/DealRepository.kt @@ -14,4 +14,6 @@ interface DealRepository { suspend fun getPotentiallyExpiredDeals(): List suspend fun getNewDeals(since: OffsetDateTime): List + + suspend fun getDealByPackageName(packageName: String): DealEntity? } diff --git a/backend/src/main/kotlin/me/sujanpoudel/playdeals/repositories/persistent/PersistentDealRepository.kt b/backend/src/main/kotlin/me/sujanpoudel/playdeals/repositories/persistent/PersistentDealRepository.kt index ca438c3..e5b3602 100644 --- a/backend/src/main/kotlin/me/sujanpoudel/playdeals/repositories/persistent/PersistentDealRepository.kt +++ b/backend/src/main/kotlin/me/sujanpoudel/playdeals/repositories/persistent/PersistentDealRepository.kt @@ -79,4 +79,14 @@ class PersistentDealRepository( .await() .map { it.asAppDeal() } } + + override suspend fun getDealByPackageName(packageName: String): DealEntity? { + return sqlClient.preparedQuery( + """ + SELECT * FROM "deal" where id = $1 + """.trimIndent() + ).exec(packageName) + .await() + .firstOrNull()?.asAppDeal() + } } diff --git a/backend/src/main/kotlin/me/sujanpoudel/playdeals/usecases/NewDealUseCase.kt b/backend/src/main/kotlin/me/sujanpoudel/playdeals/usecases/NewDealUseCase.kt index aadff5d..6c36df3 100644 --- a/backend/src/main/kotlin/me/sujanpoudel/playdeals/usecases/NewDealUseCase.kt +++ b/backend/src/main/kotlin/me/sujanpoudel/playdeals/usecases/NewDealUseCase.kt @@ -1,6 +1,7 @@ package me.sujanpoudel.playdeals.usecases import me.sujanpoudel.playdeals.jobs.AppDetailScrapper +import me.sujanpoudel.playdeals.repositories.DealRepository import org.jobrunr.scheduling.JobRequestScheduler import org.kodein.di.DI import org.kodein.di.instance @@ -11,9 +12,17 @@ class NewDealUseCase( ) : UseCase { private val jobRequestScheduler by di.instance() + private val dealsRepository by di.instance() override suspend fun doExecute(input: String) { + val existingDeal = dealsRepository.getDealByPackageName(input) + + if (existingDeal != null) { + return + } + val id = UUID.nameUUIDFromBytes(input.toByteArray()) + jobRequestScheduler.enqueue(id, AppDetailScrapper.Request(input)) } } diff --git a/backend/src/test/kotlin/me/sujanpoudel/playdeals/api/deals/NewDealApiTest.kt b/backend/src/test/kotlin/me/sujanpoudel/playdeals/api/deals/NewDealApiTest.kt index 1c8c788..860369d 100644 --- a/backend/src/test/kotlin/me/sujanpoudel/playdeals/api/deals/NewDealApiTest.kt +++ b/backend/src/test/kotlin/me/sujanpoudel/playdeals/api/deals/NewDealApiTest.kt @@ -4,11 +4,16 @@ import io.kotest.matchers.shouldBe import io.vertx.core.Vertx import io.vertx.kotlin.core.json.jsonObjectOf import io.vertx.kotlin.coroutines.await +import me.sujanpoudel.playdeals.Constants import me.sujanpoudel.playdeals.IntegrationTest +import me.sujanpoudel.playdeals.domain.NewDeal +import me.sujanpoudel.playdeals.domain.entities.DealType import me.sujanpoudel.playdeals.get +import me.sujanpoudel.playdeals.repositories.DealRepository import org.jobrunr.jobs.states.StateName import org.jobrunr.storage.StorageProvider import org.junit.jupiter.api.Test +import java.time.OffsetDateTime import java.util.UUID class NewDealApiTest(vertx: Vertx) : IntegrationTest(vertx) { @@ -53,4 +58,37 @@ class NewDealApiTest(vertx: Vertx) : IntegrationTest(vertx) { response.statusCode() shouldBe 200 } + + @Test + fun `should should 200 if the app already exists`() = runTest { + di.get() + val repository = di.get() + + val packageName = "com.example.app" + + val newDeal = NewDeal( + id = packageName, + name = "name", + icon = "icon", + images = listOf("img0", "img1"), + normalPrice = 12f, + currentPrice = 12f, + currency = "USD", + storeUrl = "store_url", + category = "unknown", + downloads = "12+", + rating = "12", + offerExpiresIn = OffsetDateTime.now(), + type = DealType.ANDROID_APP, + source = Constants.DealSources.APP_DEAL_SUBREDDIT + ) + + repository.upsert(newDeal) + + val response = httpClient.post("/api/deals") + .sendJson(jsonObjectOf("packageName" to packageName)) + .await() + + response.statusCode() shouldBe 200 + } } diff --git a/backend/src/test/kotlin/me/sujanpoudel/playdeals/repositories/PersistentDealRepositoryTest.kt b/backend/src/test/kotlin/me/sujanpoudel/playdeals/repositories/PersistentDealRepositoryTest.kt index 4998570..255d385 100644 --- a/backend/src/test/kotlin/me/sujanpoudel/playdeals/repositories/PersistentDealRepositoryTest.kt +++ b/backend/src/test/kotlin/me/sujanpoudel/playdeals/repositories/PersistentDealRepositoryTest.kt @@ -114,4 +114,25 @@ class PersistentDealRepositoryTest(vertx: Vertx) : IntegrationTest(vertx) { count.shouldContainAll(deal1, deal2) } + + @Test + fun `getDealByPackageName should return correct deal by packageName`() = runTest { + val deal0 = repository.upsert(newDeal) + + repository.upsert(newDeal.copy(id = "id_1")) + repository.upsert(newDeal.copy(id = "id_2")) + + val deal01 = repository.getDealByPackageName(deal0.id) + + deal0 shouldBe deal01 + } + + @Test + fun `getDealByPackageName should return null when there is no deal`() = runTest { + val deal0 = repository.upsert(newDeal) + + val deal1 = repository.getDealByPackageName("id_3") + + deal1 shouldBe null + } }