From 6ed2b5d31e3eaa2dc3135191b3729508a9207dcb Mon Sep 17 00:00:00 2001 From: Alexis Hernandez Date: Sat, 23 Jun 2018 23:19:12 -0500 Subject: [PATCH] server: Update "GET /addresses/:address" (#18) Now it retrieves the address balance from the database instead of the xsn server, this reduces the data because now we get the balance only and the transactions are retrieved with another endpoint. --- .../xsn/explorer/models/AddressDetails.scala | 19 ------ .../explorer/services/AddressService.scala | 15 +++-- .../app/controllers/AddressesController.scala | 4 +- server/conf/routes | 2 +- .../helpers/BalanceDummyDataHandler.scala | 18 ++++++ .../com/xsn/explorer/helpers/DataHelper.scala | 8 +-- .../helpers/TransactionDummyDataHandler.scala | 18 ++++++ .../controllers/AddressesControllerSpec.scala | 64 ++++++------------- 8 files changed, 71 insertions(+), 77 deletions(-) delete mode 100644 server/app/com/xsn/explorer/models/AddressDetails.scala create mode 100644 server/test/com/xsn/explorer/helpers/BalanceDummyDataHandler.scala create mode 100644 server/test/com/xsn/explorer/helpers/TransactionDummyDataHandler.scala diff --git a/server/app/com/xsn/explorer/models/AddressDetails.scala b/server/app/com/xsn/explorer/models/AddressDetails.scala deleted file mode 100644 index 4fc0348..0000000 --- a/server/app/com/xsn/explorer/models/AddressDetails.scala +++ /dev/null @@ -1,19 +0,0 @@ -package com.xsn.explorer.models - -import com.xsn.explorer.models.rpc.AddressBalance -import play.api.libs.json._ - -case class AddressDetails(balance: AddressBalance, transactions: List[TransactionId]) - -object AddressDetails { - - implicit val writes: Writes[AddressDetails] = Writes { obj => - val transactions = obj.transactions.map { txid => Json.toJson(txid) } - val values = Map( - "balance" -> JsNumber(obj.balance.balance), - "received" -> JsNumber(obj.balance.received), - "transactions" -> JsArray(transactions)) - - JsObject.apply(values) - } -} diff --git a/server/app/com/xsn/explorer/services/AddressService.scala b/server/app/com/xsn/explorer/services/AddressService.scala index 0dc8dd8..93ff045 100644 --- a/server/app/com/xsn/explorer/services/AddressService.scala +++ b/server/app/com/xsn/explorer/services/AddressService.scala @@ -4,21 +4,24 @@ import javax.inject.Inject import com.alexitc.playsonify.core.FutureOr.Implicits.{FutureOps, OrOps} import com.alexitc.playsonify.core.{ApplicationResult, FutureApplicationResult} +import com.xsn.explorer.data.async.BalanceFutureDataHandler import com.xsn.explorer.errors.AddressFormatError -import com.xsn.explorer.models.{Address, AddressDetails} +import com.xsn.explorer.models.{Address, Balance} import org.scalactic.{One, Or} import play.api.libs.json.JsValue import scala.concurrent.ExecutionContext -class AddressService @Inject() (xsnService: XSNService)(implicit ec: ExecutionContext) { +class AddressService @Inject() ( + xsnService: XSNService, + balanceFutureDataHandler: BalanceFutureDataHandler)( + implicit ec: ExecutionContext) { - def getDetails(addressString: String): FutureApplicationResult[AddressDetails] = { + def getBy(addressString: String): FutureApplicationResult[Balance] = { val result = for { address <- getAddress(addressString).toFutureOr - balance <- xsnService.getAddressBalance(address).toFutureOr - transactions <- xsnService.getTransactions(address).toFutureOr - } yield AddressDetails(balance, transactions) + balance <- balanceFutureDataHandler.getBy(address).toFutureOr + } yield balance result.toFuture } diff --git a/server/app/controllers/AddressesController.scala b/server/app/controllers/AddressesController.scala index c9595af..211d1ba 100644 --- a/server/app/controllers/AddressesController.scala +++ b/server/app/controllers/AddressesController.scala @@ -12,8 +12,8 @@ class AddressesController @Inject() ( cc: MyJsonControllerComponents) extends MyJsonController(cc) { - def getDetails(address: String) = publicNoInput { _ => - addressService.getDetails(address) + def getBy(address: String) = publicNoInput { _ => + addressService.getBy(address) } def getTransactions(address: String, offset: Int, limit: Int, ordering: String) = publicNoInput { _ => diff --git a/server/conf/routes b/server/conf/routes index d748df5..716e274 100644 --- a/server/conf/routes +++ b/server/conf/routes @@ -9,7 +9,7 @@ GET /transactions/:txid controllers.TransactionsController.getTransacti GET /transactions/:txid/raw controllers.TransactionsController.getRawTransaction(txid: String) POST /transactions controllers.TransactionsController.sendRawTransaction() -GET /addresses/:address controllers.AddressesController.getDetails(address: String) +GET /addresses/:address controllers.AddressesController.getBy(address: String) GET /addresses/:address/transactions controllers.AddressesController.getTransactions(address: String, offset: Int ?= 0, limit: Int ?= 10, orderBy: String ?= "") GET /addresses/:address/utxos controllers.AddressesController.getUnspentOutputs(address: String) diff --git a/server/test/com/xsn/explorer/helpers/BalanceDummyDataHandler.scala b/server/test/com/xsn/explorer/helpers/BalanceDummyDataHandler.scala new file mode 100644 index 0000000..a29341c --- /dev/null +++ b/server/test/com/xsn/explorer/helpers/BalanceDummyDataHandler.scala @@ -0,0 +1,18 @@ +package com.xsn.explorer.helpers + +import com.alexitc.playsonify.core.ApplicationResult +import com.alexitc.playsonify.models.{FieldOrdering, PaginatedQuery, PaginatedResult} +import com.xsn.explorer.data.BalanceBlockingDataHandler +import com.xsn.explorer.models.fields.BalanceField +import com.xsn.explorer.models.{Address, Balance} + +class BalanceDummyDataHandler extends BalanceBlockingDataHandler { + + override def upsert(balance: Balance): ApplicationResult[Balance] = ??? + + override def get(query: PaginatedQuery, ordering: FieldOrdering[BalanceField]): ApplicationResult[PaginatedResult[Balance]] = ??? + + override def getBy(address: Address): ApplicationResult[Balance] = ??? + + override def getNonZeroBalances(query: PaginatedQuery, ordering: FieldOrdering[BalanceField]): ApplicationResult[PaginatedResult[Balance]] = ??? +} diff --git a/server/test/com/xsn/explorer/helpers/DataHelper.scala b/server/test/com/xsn/explorer/helpers/DataHelper.scala index 7490d17..91daca0 100644 --- a/server/test/com/xsn/explorer/helpers/DataHelper.scala +++ b/server/test/com/xsn/explorer/helpers/DataHelper.scala @@ -1,7 +1,7 @@ package com.xsn.explorer.helpers -import com.xsn.explorer.models.rpc.{AddressBalance, ScriptPubKey, TransactionVOUT} -import com.xsn.explorer.models.{Address, AddressDetails, Blockhash, TransactionId} +import com.xsn.explorer.models.rpc.{ScriptPubKey, TransactionVOUT} +import com.xsn.explorer.models.{Address, Blockhash, TransactionId} object DataHelper { @@ -25,8 +25,4 @@ object DataHelper { def createScriptPubKey(scriptType: String, asm: String, address: Option[Address] = None) = { ScriptPubKey(scriptType, asm, address.toList) } - - def createAddressDetails(balance: Int, received: Int, transactions: List[TransactionId]) = { - AddressDetails(AddressBalance(BigDecimal(balance), BigDecimal(received)), transactions) - } } diff --git a/server/test/com/xsn/explorer/helpers/TransactionDummyDataHandler.scala b/server/test/com/xsn/explorer/helpers/TransactionDummyDataHandler.scala new file mode 100644 index 0000000..cecf4a4 --- /dev/null +++ b/server/test/com/xsn/explorer/helpers/TransactionDummyDataHandler.scala @@ -0,0 +1,18 @@ +package com.xsn.explorer.helpers + +import com.alexitc.playsonify.core.ApplicationResult +import com.alexitc.playsonify.models.{FieldOrdering, PaginatedQuery, PaginatedResult} +import com.xsn.explorer.data.TransactionBlockingDataHandler +import com.xsn.explorer.models._ +import com.xsn.explorer.models.fields.TransactionField + +class TransactionDummyDataHandler extends TransactionBlockingDataHandler { + + override def upsert(transaction: Transaction): ApplicationResult[Transaction] = ??? + + override def delete(transactionId: TransactionId): ApplicationResult[Transaction] = ??? + + override def deleteBy(blockhash: Blockhash): ApplicationResult[List[Transaction]] = ??? + + override def getBy(address: Address, paginatedQuery: PaginatedQuery, ordering: FieldOrdering[TransactionField]): ApplicationResult[PaginatedResult[TransactionWithValues]] = ??? +} diff --git a/server/test/controllers/AddressesControllerSpec.scala b/server/test/controllers/AddressesControllerSpec.scala index 4fee9b5..a3ada1e 100644 --- a/server/test/controllers/AddressesControllerSpec.scala +++ b/server/test/controllers/AddressesControllerSpec.scala @@ -3,15 +3,13 @@ package controllers import com.alexitc.playsonify.PublicErrorRenderer import com.alexitc.playsonify.core.{ApplicationResult, FutureApplicationResult} import com.alexitc.playsonify.models.{Count, FieldOrdering, PaginatedQuery, PaginatedResult} -import com.xsn.explorer.data.TransactionBlockingDataHandler -import com.xsn.explorer.errors.AddressFormatError -import com.xsn.explorer.helpers.{DataHelper, DummyXSNService} +import com.xsn.explorer.data.{BalanceBlockingDataHandler, TransactionBlockingDataHandler} +import com.xsn.explorer.helpers.{BalanceDummyDataHandler, DataHelper, DummyXSNService, TransactionDummyDataHandler} import com.xsn.explorer.models._ import com.xsn.explorer.models.fields.TransactionField -import com.xsn.explorer.models.rpc.AddressBalance import com.xsn.explorer.services.XSNService import controllers.common.MyAPISpec -import org.scalactic.{Good, One, Or} +import org.scalactic.Good import play.api.inject.bind import play.api.libs.json.{JsValue, Json} import play.api.test.Helpers._ @@ -22,15 +20,8 @@ class AddressesControllerSpec extends MyAPISpec { import DataHelper._ - val addressEmpty = createAddressDetails(0, 0, List()) - val addressFilled = createAddressDetails( - 100, - 200, - List( - createTransactionId("0834641a7d30d8a2d2b451617599670445ee94ed7736e146c13be260c576c641"), - createTransactionId("024aba1d535cfe5dd3ea465d46a828a57b00e1df012d7a2d158e0f7484173f7c") - ) - ) + val addressWithBalance = createAddress("XeNEPsgeWqNbrEGEN5vqv4wYcC3qQrqNyp") + val addressBalance = Balance(addressWithBalance, spent = 100, received = 200) val addressForUtxos = DataHelper.createAddress("XeNEPsgeWqNbrEGEN5vqv4wYcC3qQrqNyp") val utxosResponse = @@ -66,22 +57,6 @@ class AddressesControllerSpec extends MyAPISpec { val customXSNService = new DummyXSNService { - val map = Map( - "Xi3sQfMQsy2CzMZTrnKW6HFGp1VqFThdLw" -> addressEmpty, - "XnH3bC9NruJ4wnu4Dgi8F3wemmJtcxpKp6" -> addressFilled - ) - - override def getAddressBalance(address: Address): FutureApplicationResult[AddressBalance] = { - val maybe = map.get(address.string).map(_.balance) - val result = Or.from(maybe, One(AddressFormatError)) - Future.successful(result) - } - - override def getTransactions(address: Address): FutureApplicationResult[List[TransactionId]] = { - val maybe = map.get(address.string).map(_.transactions) - val result = Or.from(maybe, One(AddressFormatError)) - Future.successful(result) - } override def getUnspentOutputs(address: Address): FutureApplicationResult[JsValue] = { if (address == addressForUtxos) { @@ -93,13 +68,7 @@ class AddressesControllerSpec extends MyAPISpec { } } - private val customTransactionDataHandler = new TransactionBlockingDataHandler { - - override def upsert(transaction: Transaction): ApplicationResult[Transaction] = ??? - - override def delete(transactionId: TransactionId): ApplicationResult[Transaction] = ??? - - override def deleteBy(blockhash: Blockhash): ApplicationResult[List[Transaction]] = ??? + private val customTransactionDataHandler = new TransactionDummyDataHandler { override def getBy(address: Address, paginatedQuery: PaginatedQuery, ordering: FieldOrdering[TransactionField]): ApplicationResult[PaginatedResult[TransactionWithValues]] = { if (address == addressForTransactions) { @@ -110,27 +79,36 @@ class AddressesControllerSpec extends MyAPISpec { } } + private val customBalanceDataHandler = new BalanceDummyDataHandler { + override def getBy(address: Address): ApplicationResult[Balance] = { + if (address == addressWithBalance) { + Good(addressBalance) + } else { + Good(Balance(address)) + } + } + } + override val application = guiceApplicationBuilder .overrides(bind[XSNService].to(customXSNService)) .overrides(bind[TransactionBlockingDataHandler].to(customTransactionDataHandler)) + .overrides(bind[BalanceBlockingDataHandler].to(customBalanceDataHandler)) .build() "GET /addresses/:address" should { def url(address: String) = s"/addresses/$address" "retrieve address information" in { - val address = addressFilled - val response = GET(url("XnH3bC9NruJ4wnu4Dgi8F3wemmJtcxpKp6")) + val address = addressWithBalance + val response = GET(url(address.string)) status(response) mustEqual OK val json = contentAsJson(response) - (json \ "balance").as[BigDecimal] mustEqual address.balance.balance - (json \ "received").as[BigDecimal] mustEqual address.balance.received - (json \ "transactions").as[List[String]].size mustEqual address.transactions.size + (json \ "spent").as[BigDecimal] mustEqual addressBalance.spent + (json \ "received").as[BigDecimal] mustEqual addressBalance.received } "fail on bad address format" in { - val address = "XnH3bC9NruJ4wnu4Dgi8F3wemmJtcxpKp" val response = GET(url(address))