From 1e06ca91fe574833a2f463a8e4c6b4738fd03f2a Mon Sep 17 00:00:00 2001 From: Sujan Poudel Date: Thu, 4 Jan 2024 08:11:25 +0545 Subject: [PATCH 1/4] forex country info (#49) --- .../sujanpoudel/playdeals/domain/ForexRate.kt | 8 +++++- .../playdeals/jobs/ForexFetcher.kt | 24 +++++++++++++++- .../playdeals/api/forex/GetForexApiTest.kt | 28 +++++++++++++++++-- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/backend/src/main/kotlin/me/sujanpoudel/playdeals/domain/ForexRate.kt b/backend/src/main/kotlin/me/sujanpoudel/playdeals/domain/ForexRate.kt index 286e35f..f47a190 100644 --- a/backend/src/main/kotlin/me/sujanpoudel/playdeals/domain/ForexRate.kt +++ b/backend/src/main/kotlin/me/sujanpoudel/playdeals/domain/ForexRate.kt @@ -8,4 +8,10 @@ data class ForexRate( val rates: List ) -data class ConversionRate(val currency: String, val rate: Float) +data class ConversionRate( + val currency: String, + val symbol: String, + val name: String, + val flag: String, + val rate: Float +) diff --git a/backend/src/main/kotlin/me/sujanpoudel/playdeals/jobs/ForexFetcher.kt b/backend/src/main/kotlin/me/sujanpoudel/playdeals/jobs/ForexFetcher.kt index 5d87dd3..a16d17e 100644 --- a/backend/src/main/kotlin/me/sujanpoudel/playdeals/jobs/ForexFetcher.kt +++ b/backend/src/main/kotlin/me/sujanpoudel/playdeals/jobs/ForexFetcher.kt @@ -19,6 +19,8 @@ import org.kodein.di.instance import java.time.Duration import java.time.OffsetDateTime import java.time.ZoneOffset +import java.util.Currency +import java.util.Locale import java.util.UUID class ForexFetcher( @@ -57,10 +59,23 @@ class ForexFetcher( val epochSeconds = response.getLong("timestamp") val usdRate = response.getJsonObject("rates").getNumber("USD").toFloat() + val currencyLocale = Locale.getAvailableLocales().associateBy { + kotlin.runCatching { Currency.getInstance(it) }.getOrNull() ?: Locale.getDefault() + } + return ForexRate( timestamp = OffsetDateTime.ofInstant(java.time.Instant.ofEpochSecond(epochSeconds), ZoneOffset.UTC), rates = response.getJsonObject("rates").map { - ConversionRate(it.key, (it.value as Number).toFloat() / usdRate) + val currency = kotlin.runCatching { Currency.getInstance(it.key) }.getOrNull() + val locale = currencyLocale.getOrDefault(currency, Locale.US) + + ConversionRate( + currency = it.key, + symbol = currency?.getSymbol(locale) ?: "$", + name = currency?.displayName ?: it.key, + flag = locale.flagEmoji, + rate = (it.value as Number).toFloat() / usdRate + ) } ) } @@ -89,3 +104,10 @@ suspend fun KeyValuesRepository.getForexRate(): ForexRate? = get(KEY_FOREX_RATE) } suspend fun KeyValuesRepository.saveForexRate(forexRate: ForexRate) = set(KEY_FOREX_RATE, Json.encode(forexRate)) + +private val Locale.flagEmoji: String + get() { + val firstLetter = Character.codePointAt(country, 0) - 0x41 + 0x1F1E6 + val secondLetter = Character.codePointAt(country, 1) - 0x41 + 0x1F1E6 + return String(Character.toChars(firstLetter)) + String(Character.toChars(secondLetter)) + } diff --git a/backend/src/test/kotlin/me/sujanpoudel/playdeals/api/forex/GetForexApiTest.kt b/backend/src/test/kotlin/me/sujanpoudel/playdeals/api/forex/GetForexApiTest.kt index 5b40a58..4711820 100644 --- a/backend/src/test/kotlin/me/sujanpoudel/playdeals/api/forex/GetForexApiTest.kt +++ b/backend/src/test/kotlin/me/sujanpoudel/playdeals/api/forex/GetForexApiTest.kt @@ -8,17 +8,38 @@ import me.sujanpoudel.playdeals.IntegrationTest import me.sujanpoudel.playdeals.domain.ConversionRate import me.sujanpoudel.playdeals.domain.ForexRate import me.sujanpoudel.playdeals.get +import me.sujanpoudel.playdeals.jobs.getForexRate import me.sujanpoudel.playdeals.jobs.saveForexRate import me.sujanpoudel.playdeals.repositories.KeyValuesRepository import org.junit.jupiter.api.Test import java.time.OffsetDateTime +import java.time.ZoneOffset class GetForexApiTest(vertx: Vertx) : IntegrationTest(vertx) { + + @Test + fun `Key value repo should properly store the forex rate`() = runTest { + val repository = di.get() + + val forexRate = ForexRate( + timestamp = OffsetDateTime.now().withOffsetSameInstant(ZoneOffset.UTC), + rates = listOf(ConversionRate("USD", "$", "US Dollar", "🇺🇸", 1.1f)) + ) + repository.saveForexRate(forexRate) + + val savedForexRate = repository.getForexRate() + + savedForexRate shouldBe forexRate + } + @Test fun `should return forex if there is data`() = runTest { val repository = di.get() - val forexRate = ForexRate(OffsetDateTime.now(), listOf(ConversionRate("USD", 1.1f))) + val forexRate = ForexRate( + timestamp = OffsetDateTime.now().withOffsetSameInstant(ZoneOffset.UTC), + rates = listOf(ConversionRate("USD", "$", "US Dollar", "🇺🇸", 1.1f)) + ) repository.saveForexRate(forexRate) @@ -28,11 +49,14 @@ class GetForexApiTest(vertx: Vertx) : IntegrationTest(vertx) { .bodyAsJsonObject() response.getJsonObject("data").also { data -> - OffsetDateTime.parse(data.getString("timestamp")).toEpochSecond() shouldBe forexRate.timestamp.toEpochSecond() + OffsetDateTime.parse(data.getString("timestamp")) shouldBe forexRate.timestamp data.getJsonArray("rates").also { rates -> rates.size() shouldBe 1 (rates.first() as JsonObject).also { rate -> rate.getString("currency") shouldBe "USD" + rate.getString("symbol") shouldBe "$" + rate.getString("name") shouldBe "US Dollar" + rate.getString("flag") shouldBe "🇺🇸" rate.getFloat("rate") shouldBe 1.1f } } From 6ba96b4eec49f9350a617c098331cc95777f70d6 Mon Sep 17 00:00:00 2001 From: Sujan Poudel Date: Thu, 4 Jan 2024 13:23:22 +0545 Subject: [PATCH 2/4] remove currency flag (#50) --- .../sujanpoudel/playdeals/domain/ForexRate.kt | 1 - .../playdeals/jobs/ForexFetcher.kt | 29 +- backend/src/main/resources/currencies.json | 690 ++++++++++++++++++ .../playdeals/api/forex/GetForexApiTest.kt | 5 +- 4 files changed, 711 insertions(+), 14 deletions(-) create mode 100644 backend/src/main/resources/currencies.json diff --git a/backend/src/main/kotlin/me/sujanpoudel/playdeals/domain/ForexRate.kt b/backend/src/main/kotlin/me/sujanpoudel/playdeals/domain/ForexRate.kt index f47a190..c85f694 100644 --- a/backend/src/main/kotlin/me/sujanpoudel/playdeals/domain/ForexRate.kt +++ b/backend/src/main/kotlin/me/sujanpoudel/playdeals/domain/ForexRate.kt @@ -12,6 +12,5 @@ data class ConversionRate( val currency: String, val symbol: String, val name: String, - val flag: String, val rate: Float ) diff --git a/backend/src/main/kotlin/me/sujanpoudel/playdeals/jobs/ForexFetcher.kt b/backend/src/main/kotlin/me/sujanpoudel/playdeals/jobs/ForexFetcher.kt index a16d17e..6bc2757 100644 --- a/backend/src/main/kotlin/me/sujanpoudel/playdeals/jobs/ForexFetcher.kt +++ b/backend/src/main/kotlin/me/sujanpoudel/playdeals/jobs/ForexFetcher.kt @@ -1,5 +1,7 @@ package me.sujanpoudel.playdeals.jobs +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken import io.vertx.core.json.Json import io.vertx.ext.web.client.WebClient import io.vertx.ext.web.client.WebClientOptions @@ -19,10 +21,21 @@ import org.kodein.di.instance import java.time.Duration import java.time.OffsetDateTime import java.time.ZoneOffset -import java.util.Currency import java.util.Locale import java.util.UUID +data class Currency( + val name: String, + val symbol: String +) + +fun loadCurrencies(): HashMap { + return Gson().fromJson( + Thread.currentThread().contextClassLoader.getResource("currencies.json")?.readText() ?: "{}", + object : TypeToken>() {} + ) +} + class ForexFetcher( override val di: DI, private val conf: Conf @@ -48,6 +61,8 @@ class ForexFetcher( } private suspend fun getForexRates(): ForexRate { + val currencies = loadCurrencies() + val response = webClient.get("/v1/latest?access_key=${conf.forexApiKey}&format=1&base=EUR") .send() .await() @@ -59,21 +74,15 @@ class ForexFetcher( val epochSeconds = response.getLong("timestamp") val usdRate = response.getJsonObject("rates").getNumber("USD").toFloat() - val currencyLocale = Locale.getAvailableLocales().associateBy { - kotlin.runCatching { Currency.getInstance(it) }.getOrNull() ?: Locale.getDefault() - } - return ForexRate( timestamp = OffsetDateTime.ofInstant(java.time.Instant.ofEpochSecond(epochSeconds), ZoneOffset.UTC), rates = response.getJsonObject("rates").map { - val currency = kotlin.runCatching { Currency.getInstance(it.key) }.getOrNull() - val locale = currencyLocale.getOrDefault(currency, Locale.US) + val currency = currencies[it.key] ConversionRate( currency = it.key, - symbol = currency?.getSymbol(locale) ?: "$", - name = currency?.displayName ?: it.key, - flag = locale.flagEmoji, + symbol = currency?.symbol ?: "$", + name = currency?.name ?: it.key, rate = (it.value as Number).toFloat() / usdRate ) } diff --git a/backend/src/main/resources/currencies.json b/backend/src/main/resources/currencies.json new file mode 100644 index 0000000..bf00b05 --- /dev/null +++ b/backend/src/main/resources/currencies.json @@ -0,0 +1,690 @@ +{ + "FJD": { + "name": "Fijian Dollar", + "symbol": "$" + }, + "MXN": { + "name": "Mexican Peso", + "symbol": "$" + }, + "SCR": { + "name": "Seychellois Rupee", + "symbol": "Rs" + }, + "TVD": { + "name": "Tuvaluan Dollar", + "symbol": "$" + }, + "CDF": { + "name": "Congolese Franc", + "symbol": "₣" + }, + "BBD": { + "name": "Barbadian Dollar", + "symbol": "$" + }, + "GTQ": { + "name": "Guatemalan Quetzal", + "symbol": "$" + }, + "CLP": { + "name": "Chilean Peso", + "symbol": "$" + }, + "HNL": { + "name": "Honduran Lempira", + "symbol": "L" + }, + "PND": { + "name": "Pitcairn Islands Dollar", + "symbol": "$" + }, + "UGX": { + "name": "Ugandan Shilling", + "symbol": "Sh" + }, + "ZAR": { + "name": "South African Rand", + "symbol": "R" + }, + "TND": { + "name": "Tunisian Dinar", + "symbol": "د.ت." + }, + "EHP": { + "name": "Sahrawi Peseta", + "symbol": "Ptas." + }, + "STN": { + "name": "Sao Tome and Príncipe Dobra", + "symbol": "Db" + }, + "CUC": { + "name": "Cuban convertible Peso", + "symbol": "$" + }, + "BSD": { + "name": "Bahamian Dollar", + "symbol": "$" + }, + "SLL": { + "name": "Sierra Leonean Leone", + "symbol": "Le" + }, + "SDG": { + "name": "Sudanese Pound", + "symbol": "ج.س." + }, + "SLS": { + "name": "Somaliland Shilling", + "symbol": "Sl" + }, + "IQD": { + "name": "Iraqi Dinar", + "symbol": "د.ع." + }, + "CUP": { + "name": "Cuban Peso", + "symbol": "₱" + }, + "GMD": { + "name": "Gambian Dalasi", + "symbol": "D" + }, + "TWD": { + "name": "New Taiwan Dollar", + "symbol": "圓" + }, + "RSD": { + "name": "Serbian Dinar", + "symbol": "дин" + }, + "DOP": { + "name": "Dominican Peso", + "symbol": "$" + }, + "KMF": { + "name": "Comorian Franc", + "symbol": "CF" + }, + "MYR": { + "name": "Malaysian Ringgit", + "symbol": "RM" + }, + "FKP": { + "name": "Falkland Islands Pound", + "symbol": "£" + }, + "XOF": { + "name": "West African CFA Franc BCEAO", + "symbol": "₣" + }, + "GEL": { + "name": "Georgian Lari", + "symbol": "₾" + }, + "UYU": { + "name": "Uruguayan Peso", + "symbol": "$" + }, + "MAD": { + "name": "Moroccan Dirham", + "symbol": "د.م." + }, + "CVE": { + "name": "Cabo Verdean Escudo", + "symbol": "$" + }, + "TOP": { + "name": "Tongan Paʻanga", + "symbol": "PT" + }, + "AZN": { + "name": "Azerbaijani Manat", + "symbol": "₼" + }, + "OMR": { + "name": "Omani Rial", + "symbol": "ر.ع." + }, + "PGK": { + "name": "Papua New Guinean Kina", + "symbol": "K" + }, + "KES": { + "name": "Kenyan Shilling", + "symbol": "KSh" + }, + "SEK": { + "name": "Swedish Krona", + "symbol": "kr" + }, + "BTN": { + "name": "Bhutanese Ngultrum", + "symbol": "Nu." + }, + "UAH": { + "name": "Ukrainian Hryvnia", + "symbol": "грн" + }, + "GNF": { + "name": "Guinean Franc", + "symbol": "FG" + }, + "ERN": { + "name": "Eritrean Nakfa", + "symbol": "ناكفا" + }, + "MZN": { + "name": "Mozambican Metical", + "symbol": "MT" + }, + "SVC": { + "name": "Salvadoran Colón", + "symbol": "₡" + }, + "ARS": { + "name": "Argentine Peso", + "symbol": "$" + }, + "QAR": { + "name": "Qatari Riyal", + "symbol": "ر.ق." + }, + "IRR": { + "name": "Iranian Rial", + "symbol": "﷼" + }, + "CNY": { + "name": "Chinese Yuan", + "symbol": "¥元" + }, + "THB": { + "name": "Thai Baht", + "symbol": "฿" + }, + "UZS": { + "name": "Uzbekistani Som", + "symbol": "сум" + }, + "XPF": { + "name": "CFP Franc (Franc Pacifique)", + "symbol": "₣" + }, + "MRU": { + "name": "Mauritanian Ouguiya", + "symbol": "أ.م." + }, + "BDT": { + "name": "Bangladeshi Taka", + "symbol": "৳" + }, + "LYD": { + "name": "Libyan Dinar", + "symbol": "ل.د." + }, + "BMD": { + "name": "Bermudian Dollar", + "symbol": "$" + }, + "KWD": { + "name": "Kuwaiti Dinar", + "symbol": "د.ك." + }, + "PHP": { + "name": "Philippine Peso", + "symbol": "₱" + }, + "RUB": { + "name": "Russian Ruble", + "symbol": "₽" + }, + "PYG": { + "name": "Paraguayan Guaraní", + "symbol": "₲" + }, + "ISK": { + "name": "Icelandic Krona", + "symbol": "kr" + }, + "JMD": { + "name": "Jamaican Dollar", + "symbol": "$" + }, + "COP": { + "name": "Colombian Peso", + "symbol": "$" + }, + "MKD": { + "name": "Macedonian Denar", + "symbol": "ден" + }, + "USD": { + "name": "United States Dollar", + "symbol": "$" + }, + "DZD": { + "name": "Algerian Dinar", + "symbol": "د.ج." + }, + "PAB": { + "name": "Panamanian Balboa", + "symbol": "B/." + }, + "GGP": { + "name": "Guernsey Pound", + "symbol": "£" + }, + "SGD": { + "name": "Singapore Dollar", + "symbol": "$" + }, + "ETB": { + "name": "Ethiopian Birr", + "symbol": "ብር" + }, + "JEP": { + "name": "Jersey Pound", + "symbol": "£" + }, + "VED": { + "name": "Venezuelan bolívar digital", + "symbol": "Bs." + }, + "KGS": { + "name": "Kyrgyzstani Som", + "symbol": "с" + }, + "SOS": { + "name": "Somali Shilling", + "symbol": "Ssh" + }, + "VUV": { + "name": "Vanuatu Vatu", + "symbol": "VT" + }, + "LAK": { + "name": "Lao Kip", + "symbol": "₭" + }, + "BND": { + "name": "Brunei Dollar", + "symbol": "$" + }, + "Artsakh": { + "name": "Artsakh Dram", + "symbol": "դր." + }, + "XAF": { + "name": "Central African CFA Franc BEAC", + "symbol": "Fr." + }, + "LRD": { + "name": "Liberian Dollar", + "symbol": "$" + }, + "CHF": { + "name": "Swiss Franc", + "symbol": "₣" + }, + "HRK": { + "name": "Croatian Kuna", + "symbol": "kn" + }, + "ALL": { + "name": "Albanian Lek", + "symbol": "L" + }, + "DJF": { + "name": "Djiboutian Franc", + "symbol": "ف.ج." + }, + "PRB": { + "name": "Transnistrian Ruble", + "symbol": "р." + }, + "VES": { + "name": "Venezuelan Bolívar Soberano", + "symbol": "Bs.F" + }, + "ZMW": { + "name": "Zambian Kwacha", + "symbol": "ZK" + }, + "TZS": { + "name": "Tanzanian Shilling", + "symbol": "TSh" + }, + "VND": { + "name": "Vietnamese Dong", + "symbol": "₫" + }, + "AUD": { + "name": "Australian Dollar", + "symbol": "$" + }, + "ILS": { + "name": "Israeli new Shekel", + "symbol": "₪" + }, + "GHS": { + "name": "Ghanaian Cedi", + "symbol": "₵" + }, + "GYD": { + "name": "Guyanese Dollar", + "symbol": "$" + }, + "KPW": { + "name": "North Korean Won", + "symbol": "₩" + }, + "BOB": { + "name": "Bolivian Boliviano", + "symbol": "Bs." + }, + "KHR": { + "name": "Cambodian Riel", + "symbol": "៛" + }, + "MDL": { + "name": "Moldovan Leu", + "symbol": "L" + }, + "IDR": { + "name": "Indonesian Rupiah", + "symbol": "Rp" + }, + "KYD": { + "name": "Cayman Islands Dollar", + "symbol": "$" + }, + "AMD": { + "name": "Armenian Dram", + "symbol": "դր" + }, + "BWP": { + "name": "Botswana Pula", + "symbol": "P" + }, + "SHP": { + "name": "Saint Helena Pound", + "symbol": "£" + }, + "TRY": { + "name": "Turkish Lira", + "symbol": "₺" + }, + "LBP": { + "name": "Lebanese Pound", + "symbol": "ل.ل." + }, + "TJS": { + "name": "Tajikistani Somoni", + "symbol": "SM" + }, + "JOD": { + "name": "Jordanian Dinar", + "symbol": "د.أ." + }, + "AED": { + "name": "United Arab Emirates Dirham", + "symbol": "د.إ." + }, + "HKD": { + "name": "Hong Kong Dollar", + "symbol": "$" + }, + "RWF": { + "name": "Rwandan Franc", + "symbol": "R₣" + }, + "Abkhazia": { + "name": "Abkhazian Apsar", + "symbol": "" + }, + "EUR": { + "name": "Euro", + "symbol": "€" + }, + "FOK": { + "name": "Faroese Króna", + "symbol": "kr" + }, + "LSL": { + "name": "Lesotho Loti", + "symbol": "L" + }, + "ZWB": { + "name": "RTGS Dollar", + "symbol": "" + }, + "DKK": { + "name": "Danish Krone", + "symbol": "kr." + }, + "CAD": { + "name": "Canadian Dollar", + "symbol": "$" + }, + "KID": { + "name": "Kiribati Dollar", + "symbol": "$" + }, + "BGN": { + "name": "Bulgarian Lev", + "symbol": "лв." + }, + "MMK": { + "name": "Myanmar Kyat", + "symbol": "Ks" + }, + "MUR": { + "name": "Mauritian Rupee", + "symbol": "रु " + }, + "NOK": { + "name": "Norwegian Krone", + "symbol": "kr" + }, + "SYP": { + "name": "Syrian Pound", + "symbol": "ل.س." + }, + "IMP": { + "name": "Manx Pound", + "symbol": "£" + }, + "ZWL": { + "name": "Zimbabwean Dollar", + "symbol": "$" + }, + "GIP": { + "name": "Gibraltar Pound", + "symbol": "£" + }, + "RON": { + "name": "Romanian Leu", + "symbol": "L" + }, + "LKR": { + "name": "Sri Lankan Rupee", + "symbol": "රු or ரூ" + }, + "NGN": { + "name": "Nigerian Naira", + "symbol": "₦" + }, + "CRC": { + "name": "Costa Rican Colon", + "symbol": "₡" + }, + "CZK": { + "name": "Czech Koruna", + "symbol": "Kč" + }, + "PKR": { + "name": "Pakistani Rupee", + "symbol": "Rs" + }, + "XCD": { + "name": "East Caribbean Dollar", + "symbol": "$" + }, + "ANG": { + "name": "Netherlands Antillean Guilder", + "symbol": "ƒ" + }, + "HTG": { + "name": "Haitian Gourde", + "symbol": "G" + }, + "BHD": { + "name": "Bahraini Dinar", + "symbol": "د.ب." + }, + "KZT": { + "name": "Kazakhstani Tenge", + "symbol": "₸" + }, + "SRD": { + "name": "Surinamese Dollar", + "symbol": "$" + }, + "SZL": { + "name": "Swazi Lilangeni", + "symbol": "L" + }, + "SAR": { + "name": "Saudi Riyal", + "symbol": "ر.س." + }, + "TTD": { + "name": "Trinidad and Tobago Dollar", + "symbol": "$" + }, + "YER": { + "name": "Yemeni Rial", + "symbol": "ر.ي." + }, + "MVR": { + "name": "Maldivian Rufiyaa", + "symbol": ".ރ" + }, + "AFN": { + "name": "Afghan Afghani", + "symbol": "؋" + }, + "INR": { + "name": "Indian Rupee", + "symbol": "₹" + }, + "AWG": { + "name": "Aruban Florin", + "symbol": "ƒ" + }, + "KRW": { + "name": "South Korean Won", + "symbol": "₩" + }, + "NPR": { + "name": "Nepalese Rupee", + "symbol": "रू" + }, + "JPY": { + "name": "Japanese Yen", + "symbol": "¥" + }, + "MNT": { + "name": "Mongolian Tögrög", + "symbol": "₮" + }, + "AOA": { + "name": "Angolan Kwanza", + "symbol": "Kz" + }, + "PLN": { + "name": "Polish Zloty", + "symbol": "zł" + }, + "GBP": { + "name": "Pound Sterling", + "symbol": "£" + }, + "SBD": { + "name": "Solomon Islands Dollar", + "symbol": "$" + }, + "BYN": { + "name": "Belarusian Ruble", + "symbol": "руб." + }, + "HUF": { + "name": "Hungarian Forint", + "symbol": "Ft" + }, + "CKD": { + "name": "Cook Islands Dollar", + "symbol": "$" + }, + "BIF": { + "name": "Burundian Franc", + "symbol": "FBu" + }, + "MWK": { + "name": "Malawian Kwacha", + "symbol": "MK" + }, + "MGA": { + "name": "Malagasy Ariary", + "symbol": "Ar" + }, + "BZD": { + "name": "Belize Dollar", + "symbol": "$" + }, + "BAM": { + "name": "Bosnia and Herzegovina Convertible Mark", + "symbol": "КМ" + }, + "EGP": { + "name": "Egyptian Pound", + "symbol": "ج.م." + }, + "MOP": { + "name": "Macanese Pataca", + "symbol": "MOP$" + }, + "NAD": { + "name": "Namibian Dollar", + "symbol": "$" + }, + "SSP": { + "name": "South Sudanese Pound", + "symbol": "SS£" + }, + "NIO": { + "name": "Nicaraguan Córdoba", + "symbol": "C$" + }, + "PEN": { + "name": "Peruvian Sol", + "symbol": "S/." + }, + "NZD": { + "name": "New Zealand Dollar", + "symbol": "$" + }, + "WST": { + "name": "Samoan Tala", + "symbol": "ST" + }, + "TMT": { + "name": "Turkmenistan Manat", + "symbol": "T" + }, + "BRL": { + "name": "Brazilian Real", + "symbol": "R$" + } +} diff --git a/backend/src/test/kotlin/me/sujanpoudel/playdeals/api/forex/GetForexApiTest.kt b/backend/src/test/kotlin/me/sujanpoudel/playdeals/api/forex/GetForexApiTest.kt index 4711820..1f1fbc4 100644 --- a/backend/src/test/kotlin/me/sujanpoudel/playdeals/api/forex/GetForexApiTest.kt +++ b/backend/src/test/kotlin/me/sujanpoudel/playdeals/api/forex/GetForexApiTest.kt @@ -23,7 +23,7 @@ class GetForexApiTest(vertx: Vertx) : IntegrationTest(vertx) { val forexRate = ForexRate( timestamp = OffsetDateTime.now().withOffsetSameInstant(ZoneOffset.UTC), - rates = listOf(ConversionRate("USD", "$", "US Dollar", "🇺🇸", 1.1f)) + rates = listOf(ConversionRate("USD", "$", "US Dollar", 1.1f)) ) repository.saveForexRate(forexRate) @@ -38,7 +38,7 @@ class GetForexApiTest(vertx: Vertx) : IntegrationTest(vertx) { val forexRate = ForexRate( timestamp = OffsetDateTime.now().withOffsetSameInstant(ZoneOffset.UTC), - rates = listOf(ConversionRate("USD", "$", "US Dollar", "🇺🇸", 1.1f)) + rates = listOf(ConversionRate("USD", "$", "US Dollar", 1.1f)) ) repository.saveForexRate(forexRate) @@ -56,7 +56,6 @@ class GetForexApiTest(vertx: Vertx) : IntegrationTest(vertx) { rate.getString("currency") shouldBe "USD" rate.getString("symbol") shouldBe "$" rate.getString("name") shouldBe "US Dollar" - rate.getString("flag") shouldBe "🇺🇸" rate.getFloat("rate") shouldBe 1.1f } } From fe43511b0eb5afb647f25332263906b875a239d8 Mon Sep 17 00:00:00 2001 From: Sujan Poudel Date: Sun, 14 Jan 2024 18:29:28 +0545 Subject: [PATCH 3/4] fix: #51 showing same price on new deal notificatin (#52) --- .../me/sujanpoudel/playdeals/domain/entities/DealEntity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/kotlin/me/sujanpoudel/playdeals/domain/entities/DealEntity.kt b/backend/src/main/kotlin/me/sujanpoudel/playdeals/domain/entities/DealEntity.kt index 922d9d7..e4f4625 100644 --- a/backend/src/main/kotlin/me/sujanpoudel/playdeals/domain/entities/DealEntity.kt +++ b/backend/src/main/kotlin/me/sujanpoudel/playdeals/domain/entities/DealEntity.kt @@ -47,6 +47,6 @@ private fun Float.formatAsPrice(): String { return "$int.$formattedDecimal" } -fun DealEntity.formattedCurrentPrice() = "${currency.asCurrencySymbol()}${normalPrice.formatAsPrice()}" +fun DealEntity.formattedCurrentPrice() = "${currency.asCurrencySymbol()}${currentPrice.formatAsPrice()}" fun DealEntity.formattedNormalPrice() = "${currency.asCurrencySymbol()}${normalPrice.formatAsPrice()}" From 94dd013049028fb055685a9838cc424cb5755b05 Mon Sep 17 00:00:00 2001 From: Sujan Poudel Date: Sun, 26 May 2024 09:05:26 +0545 Subject: [PATCH 4/4] add job deletion for succeded and failed jobs (#53) --- .../kotlin/me/sujanpoudel/playdeals/DIConfigurer.kt | 8 ++++++-- .../playdeals/jobs/AndroidAppExpiryCheckScheduler.kt | 12 +++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/backend/src/main/kotlin/me/sujanpoudel/playdeals/DIConfigurer.kt b/backend/src/main/kotlin/me/sujanpoudel/playdeals/DIConfigurer.kt index cc1b7fa..8e36e27 100644 --- a/backend/src/main/kotlin/me/sujanpoudel/playdeals/DIConfigurer.kt +++ b/backend/src/main/kotlin/me/sujanpoudel/playdeals/DIConfigurer.kt @@ -1,5 +1,6 @@ package me.sujanpoudel.playdeals +import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.SerializationFeature import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule @@ -129,7 +130,8 @@ fun configureDI( .useJobActivator(instance()) .useBackgroundJobServer( BackgroundJobServerConfiguration.usingStandardBackgroundJobServerConfiguration() - .andDeleteSucceededJobsAfter(Duration.ofHours(6)) + .andDeleteSucceededJobsAfter(Duration.ofMinutes(10)) + .andPermanentlyDeleteDeletedJobsAfter(Duration.ofMinutes(10)) .andWorkerCount(1) .andPollIntervalInSeconds(10) ) @@ -154,7 +156,8 @@ fun configureDI( bindSingleton { AndroidAppExpiryCheckScheduler( repository = instance(), - requestScheduler = instance() + requestScheduler = instance(), + storageProvider = instance() ) } bindSingleton { @@ -193,5 +196,6 @@ private fun configureObjectMapper(): ObjectMapper { registerKotlinModule() registerModule(JavaTimeModule()) configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) } } diff --git a/backend/src/main/kotlin/me/sujanpoudel/playdeals/jobs/AndroidAppExpiryCheckScheduler.kt b/backend/src/main/kotlin/me/sujanpoudel/playdeals/jobs/AndroidAppExpiryCheckScheduler.kt index 8cc1531..c533145 100644 --- a/backend/src/main/kotlin/me/sujanpoudel/playdeals/jobs/AndroidAppExpiryCheckScheduler.kt +++ b/backend/src/main/kotlin/me/sujanpoudel/playdeals/jobs/AndroidAppExpiryCheckScheduler.kt @@ -2,16 +2,22 @@ package me.sujanpoudel.playdeals.jobs import me.sujanpoudel.playdeals.common.SIMPLE_NAME import me.sujanpoudel.playdeals.common.loggingExecutionTime +import me.sujanpoudel.playdeals.logger import me.sujanpoudel.playdeals.repositories.DealRepository import org.jobrunr.jobs.lambdas.JobRequest +import org.jobrunr.jobs.states.StateName import org.jobrunr.scheduling.JobRequestScheduler import org.jobrunr.scheduling.RecurringJobBuilder +import org.jobrunr.storage.StorageProvider import java.time.Duration +import java.time.Instant +import java.time.temporal.ChronoUnit import java.util.UUID class AndroidAppExpiryCheckScheduler( private val repository: DealRepository, - private val requestScheduler: JobRequestScheduler + private val requestScheduler: JobRequestScheduler, + private val storageProvider: StorageProvider ) : CoJobRequestHandler() { override suspend fun handleRequest(jobRequest: Request): Unit = loggingExecutionTime( @@ -21,6 +27,10 @@ class AndroidAppExpiryCheckScheduler( .map { AppDetailScrapper.Request(it.id) } requestScheduler.enqueue(apps) + + val lastUpdatedTime = Instant.now().minus(1, ChronoUnit.HOURS) + val jobs = storageProvider.deleteJobsPermanently(StateName.FAILED, lastUpdatedTime) + logger.info("deleted FAILED `$jobs`") } class Request private constructor() : JobRequest {