Browse Source

server: Add endpoint "GET /addresses/:address/utxos"

This is a part for #23, it allows to retrieve the unspent
outputs for the given address.
prometheus-integration
Alexis Hernandez 7 years ago
parent
commit
5a7fd8a81d
  1. 23
      server/app/com/xsn/explorer/services/AddressService.scala
  2. 4
      server/app/controllers/AddressesController.scala
  3. 1
      server/conf/routes
  4. 47
      server/test/controllers/AddressesControllerSpec.scala

23
server/app/com/xsn/explorer/services/AddressService.scala

@ -2,11 +2,12 @@ package com.xsn.explorer.services
import javax.inject.Inject
import com.alexitc.playsonify.core.FutureApplicationResult
import com.alexitc.playsonify.core.FutureOr.Implicits.{FutureOps, OrOps}
import com.alexitc.playsonify.core.{ApplicationResult, FutureApplicationResult}
import com.xsn.explorer.errors.AddressFormatError
import com.xsn.explorer.models.{Address, AddressDetails}
import org.scalactic.{One, Or}
import play.api.libs.json.JsValue
import scala.concurrent.ExecutionContext
@ -14,15 +15,25 @@ class AddressService @Inject() (xsnService: XSNService)(implicit ec: ExecutionCo
def getDetails(addressString: String): FutureApplicationResult[AddressDetails] = {
val result = for {
address <- {
val maybe = Address.from(addressString)
Or.from(maybe, One(AddressFormatError)).toFutureOr
}
address <- getAddress(addressString).toFutureOr
balance <- xsnService.getAddressBalance(address).toFutureOr
transactions <- xsnService.getTransactions(address).toFutureOr
} yield AddressDetails(balance, transactions)
result.toFuture
}
def getUnspentOutputs(addressString: String): FutureApplicationResult[JsValue] = {
val result = for {
address <- getAddress(addressString).toFutureOr
outputs <- xsnService.getUnspentOutputs(address).toFutureOr
} yield outputs
result.toFuture
}
private def getAddress(addressString: String): ApplicationResult[Address] = {
val maybe = Address.from(addressString)
Or.from(maybe, One(AddressFormatError))
}
}

4
server/app/controllers/AddressesController.scala

@ -21,4 +21,8 @@ class AddressesController @Inject() (
transactionService.getTransactions(address, paginatedQuery)
}
def getUnspentOutputs(address: String) = publicNoInput { _ =>
addressService.getUnspentOutputs(address)
}
}

1
server/conf/routes

@ -10,6 +10,7 @@ GET /transactions/:txid/raw controllers.TransactionsController.getRawTransa
GET /addresses/:address controllers.AddressesController.getDetails(address: String)
GET /addresses/:address/transactions controllers.AddressesController.getTransactions(address: String, offset: Int ?= 0, limit: Int ?= 10)
GET /addresses/:address/utxos controllers.AddressesController.getUnspentOutputs(address: String)
GET /blocks controllers.BlocksController.getLatestBlocks()
GET /blocks/:query controllers.BlocksController.getDetails(query: String)

47
server/test/controllers/AddressesControllerSpec.scala

@ -8,9 +8,9 @@ import com.xsn.explorer.models._
import com.xsn.explorer.models.rpc.AddressBalance
import com.xsn.explorer.services.XSNService
import controllers.common.MyAPISpec
import org.scalactic.{One, Or}
import org.scalactic.{Good, One, Or}
import play.api.inject.bind
import play.api.libs.json.JsValue
import play.api.libs.json.{JsValue, Json}
import play.api.test.Helpers._
import scala.concurrent.Future
@ -29,6 +29,29 @@ class AddressesControllerSpec extends MyAPISpec {
)
)
val addressForUtxos = DataHelper.createAddress("XeNEPsgeWqNbrEGEN5vqv4wYcC3qQrqNyp")
val utxosResponse =
"""
|[
| {
| "address": "XeNEPsgeWqNbrEGEN5vqv4wYcC3qQrqNyp",
| "height": 22451,
| "outputIndex": 0,
| "satoshis": 1500000000000,
| "script": "76a914285b6f1ccacea0059ff5393cb4eb2f0569e2b3e988ac",
| "txid": "ea837f2011974b6a1a2fa077dc33684932c514a4ec6febc10e1a19ebe1336539"
| },
| {
| "address": "XeNEPsgeWqNbrEGEN5vqv4wYcC3qQrqNyp",
| "height": 25093,
| "outputIndex": 3,
| "satoshis": 2250000000,
| "script": "76a914285b6f1ccacea0059ff5393cb4eb2f0569e2b3e988ac",
| "txid": "96a06b802d1c15818a42aa9b46dd2e236cde746000d35f74d3eb940ab9d5694d"
| }
|]
""".stripMargin
val customXSNService = new DummyXSNService {
val map = Map(
"Xi3sQfMQsy2CzMZTrnKW6HFGp1VqFThdLw" -> addressEmpty,
@ -46,6 +69,15 @@ class AddressesControllerSpec extends MyAPISpec {
val result = Or.from(maybe, One(AddressFormatError))
Future.successful(result)
}
override def getUnspentOutputs(address: Address): FutureApplicationResult[JsValue] = {
if (address == addressForUtxos) {
val result = Good(Json.parse(utxosResponse))
Future.successful(result)
} else {
super.getUnspentOutputs(address)
}
}
}
override val application = guiceApplicationBuilder
@ -82,4 +114,15 @@ class AddressesControllerSpec extends MyAPISpec {
(error \ "field").as[String] mustEqual "address"
}
}
"GET /addresses/:address/utxos" should {
def url(address: String) = s"/addresses/$address/utxos"
"return an array with the result" in {
val response = GET(url(addressForUtxos.string))
status(response) mustEqual OK
contentAsJson(response) mustEqual Json.parse(utxosResponse)
}
}
}

Loading…
Cancel
Save