Improved exceptions.

This commit is contained in:
Erik C. Thauvin 2021-05-09 17:54:48 -07:00
parent 4209cb83c5
commit 358b8b4582
4 changed files with 40 additions and 22 deletions

View file

@ -2,6 +2,7 @@
<SmellBaseline> <SmellBaseline>
<ManuallySuppressedIssues/> <ManuallySuppressedIssues/>
<CurrentIssues> <CurrentIssues>
<ID>ThrowsCount:CryptoPrice.kt$CryptoPrice.Companion$ @JvmStatic @JvmOverloads @Throws(CryptoException::class) fun apiCall(paths: List&lt;String>, params: Map&lt;String, String> = emptyMap()): String</ID> <ID>ThrowsCount:CryptoPrice.kt$CryptoPrice.Companion$ @JvmStatic @JvmOverloads @Throws(CryptoException::class, IOException::class) fun apiCall(paths: List&lt;String>, params: Map&lt;String, String> = emptyMap()): String</ID>
<ID>ThrowsCount:CryptoPrice.kt$CryptoPrice.Companion$@JvmStatic @Throws(CryptoException::class) fun String.toPrice(): CryptoPrice</ID>
</CurrentIssues> </CurrentIssues>
</SmellBaseline> </SmellBaseline>

View file

@ -3,6 +3,7 @@ package com.example;
import net.thauvin.erik.crypto.CryptoPrice; import net.thauvin.erik.crypto.CryptoPrice;
import net.thauvin.erik.crypto.CryptoException; import net.thauvin.erik.crypto.CryptoException;
import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -35,7 +36,7 @@ public class CryptoPriceSample {
final CryptoPrice gbpPrice = CryptoPrice.marketPrice("ETH", "GBP"); final CryptoPrice gbpPrice = CryptoPrice.marketPrice("ETH", "GBP");
System.out.println("The current Ethereum price is " + gbpPrice.getAmount() + " in Pound sterling"); System.out.println("The current Ethereum price is " + gbpPrice.getAmount() + " in Pound sterling");
} catch (CryptoException e) { } catch (CryptoException | IOException e) {
System.err.println(e.getMessage()); System.err.println(e.getMessage());
} }
} }

View file

@ -35,7 +35,9 @@ package net.thauvin.erik.crypto
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import org.json.JSONException
import org.json.JSONObject import org.json.JSONObject
import java.io.IOException
import java.time.LocalDate import java.time.LocalDate
/** /**
@ -49,15 +51,21 @@ open class CryptoPrice(val base: String, val currency: String, val amount: Doubl
@JvmStatic @JvmStatic
@Throws(CryptoException::class) @Throws(CryptoException::class)
fun String.toPrice(): CryptoPrice { fun String.toPrice(): CryptoPrice {
val json = JSONObject(this) try {
if (json.has("data")) { val json = JSONObject(this)
with(json.getJSONObject("data")) { if (json.has("data")) {
return CryptoPrice( with(json.getJSONObject("data")) {
getString("base"), getString("currency"), getString("amount").toDouble() return CryptoPrice(
) getString("base"), getString("currency"), getString("amount").toDouble()
)
}
} else {
throw CryptoException(message = "Missing price data.")
} }
} else { } catch (e: NumberFormatException) {
throw CryptoException(message = "Missing JSON data.") throw CryptoException(message = "Could not convert price data to number.", cause = e)
} catch (e: JSONException) {
throw CryptoException(message = "Could not parse price data.", cause = e)
} }
} }
@ -66,10 +74,11 @@ open class CryptoPrice(val base: String, val currency: String, val amount: Doubl
*/ */
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
@Throws(CryptoException::class) @Throws(CryptoException::class, IOException::class)
fun apiCall(paths: List<String>, params: Map<String, String> = emptyMap()): String { fun apiCall(paths: List<String>, params: Map<String, String> = emptyMap()): String {
val client = OkHttpClient() val client = OkHttpClient()
val url = COINBASE_API_URL.toHttpUrl().newBuilder() val url = COINBASE_API_URL.toHttpUrl().newBuilder()
paths.forEach { paths.forEach {
url.addPathSegment(it) url.addPathSegment(it)
} }
@ -81,19 +90,19 @@ open class CryptoPrice(val base: String, val currency: String, val amount: Doubl
val response = client.newCall(request).execute() val response = client.newCall(request).execute()
val body = response.body?.string() val body = response.body?.string()
if (body != null) { if (body != null) {
val json = JSONObject(body) try {
if (!response.isSuccessful) { val json = JSONObject(body)
if (json.has("errors")) { if (response.isSuccessful) {
return body
} else {
val data = json.getJSONArray("errors") val data = json.getJSONArray("errors")
throw CryptoException(response.code, data.getJSONObject(0).getString("message")) throw CryptoException(response.code, data.getJSONObject(0).getString("message"))
} else {
throw CryptoException(response.code, "Invalid API response.")
} }
} else { } catch (e: JSONException) {
return body throw CryptoException(response.code, "Could not parse data.", e)
} }
} else { } else {
throw CryptoException(response.code, "Empty API response.") throw CryptoException(response.code, "Empty response.")
} }
} }
@ -104,11 +113,11 @@ open class CryptoPrice(val base: String, val currency: String, val amount: Doubl
} }
/** /**
* Retrieves the current market price. * Retrieve the current market price.
*/ */
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
@Throws(CryptoException::class) @Throws(CryptoException::class, IOException::class)
fun marketPrice(base: String, currency: String = "USD", date: LocalDate? = null): CryptoPrice { fun marketPrice(base: String, currency: String = "USD", date: LocalDate? = null): CryptoPrice {
val params = if (date != null) mapOf("date" to "$date") else emptyMap() val params = if (date != null) mapOf("date" to "$date") else emptyMap()
val body = apiCall(listOf("prices", "$base-$currency", "spot"), params) val body = apiCall(listOf("prices", "$base-$currency", "spot"), params)

View file

@ -74,9 +74,16 @@ class CryptoPriceTest {
@Throws(CryptoException::class) @Throws(CryptoException::class)
fun testToPrice() { fun testToPrice() {
val d = 57515.69 val d = 57515.69
val price = "{\"data\":{\"base\":\"BTC\",\"currency\":\"USD\",\"amount\":\"$d\"}}".toPrice() val json = "{\"data\":{\"base\":\"BTC\",\"currency\":\"USD\",\"amount\":\"$d\"}}"
val price = json.toPrice()
assertEquals(price.base, "BTC", "base is BTC") assertEquals(price.base, "BTC", "base is BTC")
assertEquals(price.currency, "USD", "currency is USD") assertEquals(price.currency, "USD", "currency is USD")
assertEquals(price.amount, d, "amount is 57515.69") assertEquals(price.amount, d, "amount is 57515.69")
assertFailsWith(
message = "double convertion did not fail",
exceptionClass = CryptoException::class,
block = { json.replace("5", "a").toPrice() }
)
} }
} }