diff --git a/server/app/com/xsn/explorer/models/AddressDetails.scala b/server/app/com/xsn/explorer/models/AddressDetails.scala index 1b50fbe..4fc0348 100644 --- a/server/app/com/xsn/explorer/models/AddressDetails.scala +++ b/server/app/com/xsn/explorer/models/AddressDetails.scala @@ -3,15 +3,16 @@ package com.xsn.explorer.models import com.xsn.explorer.models.rpc.AddressBalance import play.api.libs.json._ -case class AddressDetails(balance: AddressBalance, transactionCount: Int) +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), - "transactionCount" -> JsNumber(obj.transactionCount)) + "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 c3a314b..65d5e93 100644 --- a/server/app/com/xsn/explorer/services/AddressService.scala +++ b/server/app/com/xsn/explorer/services/AddressService.scala @@ -20,8 +20,8 @@ class AddressService @Inject() (xsnService: XSNService)(implicit ec: ExecutionCo } balance <- xsnService.getAddressBalance(address).toFutureOr - transactionCount <- xsnService.getTransactionCount(address).toFutureOr - } yield AddressDetails(balance, transactionCount) + transactions <- xsnService.getTransactions(address).toFutureOr + } yield AddressDetails(balance, transactions) result.toFuture } diff --git a/server/app/com/xsn/explorer/services/XSNService.scala b/server/app/com/xsn/explorer/services/XSNService.scala index e80c1a0..27794a8 100644 --- a/server/app/com/xsn/explorer/services/XSNService.scala +++ b/server/app/com/xsn/explorer/services/XSNService.scala @@ -22,7 +22,7 @@ trait XSNService { def getAddressBalance(address: Address): FutureApplicationResult[AddressBalance] - def getTransactionCount(address: Address): FutureApplicationResult[Int] + def getTransactions(address: Address): FutureApplicationResult[List[TransactionId]] def getBlock(blockhash: Blockhash): FutureApplicationResult[Block] } @@ -83,7 +83,7 @@ class XSNServiceRPCImpl @Inject() ( } } - override def getTransactionCount(address: Address): FutureApplicationResult[Int] = { + override def getTransactions(address: Address): FutureApplicationResult[List[TransactionId]] = { val body = s""" |{ | "jsonrpc": "1.0", @@ -101,8 +101,8 @@ class XSNServiceRPCImpl @Inject() ( .post(body) .map { response => - val maybe = getResult[List[JsValue]](response, errorCodeMapper) - maybe.map(_.map(_.size)).getOrElse { + val maybe = getResult[List[TransactionId]](response, errorCodeMapper) + maybe.getOrElse { logger.warn(s"Unexpected response from XSN Server, status = ${response.status}, address = ${address.string}, response = ${response.body}") Bad(XSNUnexpectedResponseError).accumulating diff --git a/server/test/com/xsn/explorer/helpers/DataHelper.scala b/server/test/com/xsn/explorer/helpers/DataHelper.scala index f8824ec..ad8bb7a 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.{ScriptPubKey, TransactionVOUT} -import com.xsn.explorer.models.{Address, TransactionId} +import com.xsn.explorer.models.rpc.{AddressBalance, ScriptPubKey, TransactionVOUT} +import com.xsn.explorer.models.{Address, AddressDetails, TransactionId} object DataHelper { @@ -23,4 +23,8 @@ 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/DummyXSNService.scala b/server/test/com/xsn/explorer/helpers/DummyXSNService.scala index 3539fe1..2a96de1 100644 --- a/server/test/com/xsn/explorer/helpers/DummyXSNService.scala +++ b/server/test/com/xsn/explorer/helpers/DummyXSNService.scala @@ -9,6 +9,6 @@ class DummyXSNService extends XSNService { override def getTransaction(txid: TransactionId): FutureApplicationResult[Transaction] = ??? override def getAddressBalance(address: Address): FutureApplicationResult[AddressBalance] = ??? - override def getTransactionCount(address: Address): FutureApplicationResult[Int] = ??? + override def getTransactions(address: Address): FutureApplicationResult[List[TransactionId]] = ??? override def getBlock(blockhash: Blockhash): FutureApplicationResult[Block] = ??? } diff --git a/server/test/com/xsn/explorer/services/XSNServiceRPCImplSpec.scala b/server/test/com/xsn/explorer/services/XSNServiceRPCImplSpec.scala index bebf245..7f6906a 100644 --- a/server/test/com/xsn/explorer/services/XSNServiceRPCImplSpec.scala +++ b/server/test/com/xsn/explorer/services/XSNServiceRPCImplSpec.scala @@ -6,7 +6,7 @@ import com.xsn.explorer.helpers.Executors import com.xsn.explorer.models.{Address, Blockhash, TransactionId} import org.mockito.ArgumentMatchers._ import org.mockito.Mockito._ -import org.scalactic.{Bad, Good} +import org.scalactic.Bad import org.scalatest.concurrent.ScalaFutures import org.scalatest.mockito.MockitoSugar import org.scalatest.{MustMatchers, OptionValues, WordSpec} @@ -279,8 +279,8 @@ class XSNServiceRPCImplSpec extends WordSpec with MustMatchers with ScalaFutures } } - "getTransactionCount" should { - "return the number of transactions" in { + "getTransactions" should { + "return the transactions" in { val responseBody = """ |{ @@ -308,8 +308,9 @@ class XSNServiceRPCImplSpec extends WordSpec with MustMatchers with ScalaFutures when(response.json).thenReturn(json) when(request.post[String](anyString())(any())).thenReturn(Future.successful(response)) - whenReady(service.getTransactionCount(address)) { result => - result mustEqual Good(9) + whenReady(service.getTransactions(address)) { result => + result.isGood mustEqual true + result.get.size mustEqual 9 } } @@ -324,7 +325,7 @@ class XSNServiceRPCImplSpec extends WordSpec with MustMatchers with ScalaFutures when(response.json).thenReturn(json) when(request.post[String](anyString())(any())).thenReturn(Future.successful(response)) - whenReady(service.getTransactionCount(address)) { result => + whenReady(service.getTransactions(address)) { result => result mustEqual Bad(AddressFormatError).accumulating } } diff --git a/server/test/controllers/AddressesControllerSpec.scala b/server/test/controllers/AddressesControllerSpec.scala index dab25b3..b825ca7 100644 --- a/server/test/controllers/AddressesControllerSpec.scala +++ b/server/test/controllers/AddressesControllerSpec.scala @@ -3,7 +3,7 @@ package controllers import com.alexitc.playsonify.PublicErrorRenderer import com.alexitc.playsonify.core.FutureApplicationResult import com.xsn.explorer.errors.AddressFormatError -import com.xsn.explorer.helpers.DummyXSNService +import com.xsn.explorer.helpers.{DataHelper, DummyXSNService} import com.xsn.explorer.models._ import com.xsn.explorer.models.rpc.AddressBalance import com.xsn.explorer.services.XSNService @@ -15,15 +15,19 @@ import play.api.test.Helpers._ import scala.concurrent.Future - class AddressesControllerSpec extends MyAPISpec { - def addressDetails(balance: Int, received: Int, txCount: Int) = { - AddressDetails(AddressBalance(BigDecimal(balance), BigDecimal(received)), txCount) - } + import DataHelper._ - val addressEmpty = addressDetails(0, 0, 0) - val addressFilled = addressDetails(100, 200, 25) + val addressEmpty = createAddressDetails(0, 0, List()) + val addressFilled = createAddressDetails( + 100, + 200, + List( + createTransactionId("0834641a7d30d8a2d2b451617599670445ee94ed7736e146c13be260c576c641"), + createTransactionId("024aba1d535cfe5dd3ea465d46a828a57b00e1df012d7a2d158e0f7484173f7c") + ) + ) val customXSNService = new DummyXSNService { val map = Map( @@ -37,8 +41,8 @@ class AddressesControllerSpec extends MyAPISpec { Future.successful(result) } - override def getTransactionCount(address: Address): FutureApplicationResult[Int] = { - val maybe = map.get(address.string).map(_.transactionCount) + 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) } @@ -59,7 +63,7 @@ class AddressesControllerSpec extends MyAPISpec { val json = contentAsJson(response) (json \ "balance").as[BigDecimal] mustEqual address.balance.balance (json \ "received").as[BigDecimal] mustEqual address.balance.received - (json \ "transactionCount").as[Int] mustEqual address.transactionCount + (json \ "transactions").as[List[String]].size mustEqual address.transactions.size } "fail on bad address format" in {