Browse Source

server: Add getBlock method to XSNService

scalafmt-draft
Alexis Hernandez 7 years ago
parent
commit
8fd57f92f7
  1. 24
      server/app/com/xsn/explorer/errors/blockErrors.scala
  2. 23
      server/app/com/xsn/explorer/services/XSNService.scala
  3. 3
      server/conf/messages
  4. 3
      server/test/com/xsn/explorer/helpers/DummyXSNService.scala
  5. 68
      server/test/com/xsn/explorer/services/XSNServiceRPCImplSpec.scala

24
server/app/com/xsn/explorer/errors/blockErrors.scala

@ -0,0 +1,24 @@
package com.xsn.explorer.errors
import com.alexitc.playsonify.models.{FieldValidationError, InputValidationError, PublicError}
import play.api.i18n.{Lang, MessagesApi}
sealed trait BlockError
case object BlockhashFormatError extends BlockError with InputValidationError {
override def toPublicErrorList(messagesApi: MessagesApi)(implicit lang: Lang): List[PublicError] = {
val message = messagesApi("error.block.format")
val error = FieldValidationError("blockhash", message)
List(error)
}
}
case object BlockNotFoundError extends BlockError with InputValidationError {
override def toPublicErrorList(messagesApi: MessagesApi)(implicit lang: Lang): List[PublicError] = {
val message = messagesApi("error.block.notFound")
val error = FieldValidationError("blockhash", message)
List(error)
}
}

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

@ -5,9 +5,9 @@ import javax.inject.Inject
import com.alexitc.playsonify.core.{ApplicationResult, FutureApplicationResult}
import com.alexitc.playsonify.models.ApplicationError
import com.xsn.explorer.config.RPCConfig
import com.xsn.explorer.errors.{AddressFormatError, TransactionNotFoundError, XSNMessageError, XSNUnexpectedResponseError}
import com.xsn.explorer.errors._
import com.xsn.explorer.executors.ExternalServiceExecutionContext
import com.xsn.explorer.models.{Address, AddressBalance, Transaction, TransactionId}
import com.xsn.explorer.models._
import org.scalactic.{Bad, Good}
import org.slf4j.LoggerFactory
import play.api.libs.json.{JsNull, JsValue, Reads}
@ -22,6 +22,8 @@ trait XSNService {
def getAddressBalance(address: Address): FutureApplicationResult[AddressBalance]
def getTransactionCount(address: Address): FutureApplicationResult[Int]
def getBlock(blockhash: Blockhash): FutureApplicationResult[Block]
}
class XSNServiceRPCImpl @Inject() (
@ -107,6 +109,23 @@ class XSNServiceRPCImpl @Inject() (
}
}
override def getBlock(blockhash: Blockhash): FutureApplicationResult[Block] = {
val errorCodeMapper = Map(-5 -> BlockNotFoundError)
val body = s"""{ "jsonrpc": "1.0", "method": "getblock", "params": ["${blockhash.string}"] }"""
server
.post(body)
.map { response =>
val maybe = getResult[Block](response, errorCodeMapper)
maybe.getOrElse {
logger.warn(s"Unexpected response from XSN Server, txid = ${blockhash.string}, status = ${response.status}, response = ${response.body}")
Bad(XSNUnexpectedResponseError).accumulating
}
}
}
private def mapError(json: JsValue, errorCodeMapper: Map[Int, ApplicationError]): Option[ApplicationError] = {
val jsonErrorMaybe = (json \ "error")
.asOpt[JsValue]

3
server/conf/messages

@ -6,3 +6,6 @@ error.transaction.format=Invalid transaction format
error.transaction.notFound=Transaction not found
error.address.format=Invalid address format
error.block.format=Invalid blockhash format
error.block.notFound=Block not found

3
server/test/com/xsn/explorer/helpers/DummyXSNService.scala

@ -1,7 +1,7 @@
package com.xsn.explorer.helpers
import com.alexitc.playsonify.core.FutureApplicationResult
import com.xsn.explorer.models.{Address, AddressBalance, Transaction, TransactionId}
import com.xsn.explorer.models._
import com.xsn.explorer.services.XSNService
class DummyXSNService extends XSNService {
@ -9,4 +9,5 @@ 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 getBlock(blockhash: Blockhash): FutureApplicationResult[Block] = ???
}

68
server/test/com/xsn/explorer/services/XSNServiceRPCImplSpec.scala

@ -1,9 +1,9 @@
package com.xsn.explorer.services
import com.xsn.explorer.config.RPCConfig
import com.xsn.explorer.errors.{AddressFormatError, TransactionNotFoundError, XSNMessageError, XSNUnexpectedResponseError}
import com.xsn.explorer.errors._
import com.xsn.explorer.helpers.Executors
import com.xsn.explorer.models.{Address, TransactionId}
import com.xsn.explorer.models.{Address, Blockhash, TransactionId}
import org.mockito.ArgumentMatchers._
import org.mockito.Mockito._
import org.scalactic.{Bad, Good}
@ -329,4 +329,68 @@ class XSNServiceRPCImplSpec extends WordSpec with MustMatchers with ScalaFutures
}
}
}
"getBlock" should {
"return a block" in {
val responseBody =
"""
|{
| "result": {
| "hash": "b72dd1655408e9307ef5874be20422ee71029333283e2360975bc6073bdb2b81",
| "confirmations": 11163,
| "size": 478,
| "height": 809,
| "version": 536870912,
| "merkleroot": "598cc6ba8238d87641b0dbd02485b7d635b5417429df3145c98c3ff8779ab4b8",
| "tx": [
| "7f12adbb63d443502cf151c76946d5faa0b1c662a5d67afc7da085c74e06f1ce",
| "0834641a7d30d8a2d2b451617599670445ee94ed7736e146c13be260c576c641"
| ],
| "time": 1520318120,
| "mediantime": 1520318054,
| "nonce": 0,
| "bits": "1d011212",
| "difficulty": 0.9340526210769362,
| "chainwork": "00000000000000000000000000000000000000000000000000000084c71ff420",
| "previousblockhash": "9490ce5d14bb5e79a790ddede03fc3a9bde3f7156f34a57ae3ceb56ae7426c14",
| "nextblockhash": "f6e3199c241131e79640fe027a6ef993c02b3520c3d4ba08cd67abfbb98ec07e"
| },
| "error": null,
| "id": null
|}
|""".stripMargin
val blockhash = Blockhash.from("b72dd1655408e9307ef5874be20422ee71029333283e2360975bc6073bdb2b81").get
val json = Json.parse(responseBody)
when(response.status).thenReturn(200)
when(response.json).thenReturn(json)
when(request.post[String](anyString())(any())).thenReturn(Future.successful(response))
whenReady(service.getBlock(blockhash)) { result =>
result.isGood mustEqual true
val block = result.get
block.hash.string mustEqual "b72dd1655408e9307ef5874be20422ee71029333283e2360975bc6073bdb2b81"
block.transactions.size mustEqual 2
}
}
"fail on unknown block" in {
val responseBody = """{"result":null,"error":{"code":-5,"message":"Block not found"},"id":null}"""
val blockhash = Blockhash.from("b72dd1655408e9307ef5874be20422ee71029333283e2360975bc6073bdb2b80").get
val json = Json.parse(responseBody)
when(response.status).thenReturn(200)
when(response.json).thenReturn(json)
when(request.post[String](anyString())(any())).thenReturn(Future.successful(response))
whenReady(service.getBlock(blockhash)) { result =>
result mustEqual Bad(BlockNotFoundError).accumulating
}
}
}
}

Loading…
Cancel
Save