Browse Source

server: Add "GET /masternodes/:ip"

scalafmt-draft
Alexis Hernandez 7 years ago
parent
commit
6e6ed7abbe
  1. 15
      server/app/com/xsn/explorer/errors/ipAddressErrors.scala
  2. 19
      server/app/com/xsn/explorer/services/MasternodeService.scala
  3. 4
      server/app/controllers/MasternodesController.scala
  4. 2
      server/conf/messages
  5. 1
      server/conf/routes
  6. 63
      server/test/controllers/MasternodesControllerSpec.scala

15
server/app/com/xsn/explorer/errors/ipAddressErrors.scala

@ -0,0 +1,15 @@
package com.xsn.explorer.errors
import com.alexitc.playsonify.models.{FieldValidationError, InputValidationError, PublicError}
import play.api.i18n.{Lang, MessagesApi}
trait IPAddressError
case object IPAddressFormatError extends IPAddressError with InputValidationError {
override def toPublicErrorList(messagesApi: MessagesApi)(implicit lang: Lang): List[PublicError] = {
val message = messagesApi("error.ipAddress.invalid")
val error = FieldValidationError("ip", message)
List(error)
}
}

19
server/app/com/xsn/explorer/services/MasternodeService.scala

@ -3,12 +3,15 @@ package com.xsn.explorer.services
import javax.inject.Inject import javax.inject.Inject
import com.alexitc.playsonify.core.FutureOr.Implicits.{FutureOps, OrOps} import com.alexitc.playsonify.core.FutureOr.Implicits.{FutureOps, OrOps}
import com.alexitc.playsonify.core.FuturePaginatedResult import com.alexitc.playsonify.core.{FutureApplicationResult, FuturePaginatedResult}
import com.alexitc.playsonify.models._ import com.alexitc.playsonify.models._
import com.alexitc.playsonify.validators.PaginatedQueryValidator import com.alexitc.playsonify.validators.PaginatedQueryValidator
import com.xsn.explorer.errors.IPAddressFormatError
import com.xsn.explorer.models.IPAddress
import com.xsn.explorer.models.fields.MasternodeField import com.xsn.explorer.models.fields.MasternodeField
import com.xsn.explorer.models.rpc.Masternode import com.xsn.explorer.models.rpc.Masternode
import com.xsn.explorer.parsers.MasternodeOrderingParser import com.xsn.explorer.parsers.MasternodeOrderingParser
import org.scalactic.{Bad, Good}
import scala.concurrent.ExecutionContext import scala.concurrent.ExecutionContext
@ -28,6 +31,20 @@ class MasternodeService @Inject() (
result.toFuture result.toFuture
} }
def getMasternode(ipAddressString: String): FutureApplicationResult[Masternode] = {
val result = for {
ipAddress <- IPAddress
.from(ipAddressString)
.map(Good(_))
.getOrElse(Bad(IPAddressFormatError).accumulating)
.toFutureOr
masternode <- xsnService.getMasternode(ipAddress).toFutureOr
} yield masternode
result.toFuture
}
private def build(list: List[Masternode], query: PaginatedQuery, ordering: FieldOrdering[MasternodeField]) = { private def build(list: List[Masternode], query: PaginatedQuery, ordering: FieldOrdering[MasternodeField]) = {
val partial = sort(list, ordering) val partial = sort(list, ordering)
.slice(query.offset.int, query.offset.int + query.limit.int) .slice(query.offset.int, query.offset.int + query.limit.int)

4
server/app/controllers/MasternodesController.scala

@ -17,4 +17,8 @@ class MasternodesController @Inject() (
masternodeService.getMasternodes(paginatedQuery, orderingQuery) masternodeService.getMasternodes(paginatedQuery, orderingQuery)
} }
def getBy(ipAddress: String) = publicNoInput { _ =>
masternodeService.getMasternode(ipAddress)
}
} }

2
server/conf/messages

@ -12,6 +12,8 @@ error.block.notFound=Block not found
error.masternode.notFound=Masternode not found error.masternode.notFound=Masternode not found
error.ipAddress.invalid=Invalid ip address
error.paginatedQuery.offset.invalid=Invalid offset, it should be a number greater or equal than 0 error.paginatedQuery.offset.invalid=Invalid offset, it should be a number greater or equal than 0
error.paginatedQuery.limit.invalid=Invalid limit, it should be a number between 1 and and {0} error.paginatedQuery.limit.invalid=Invalid limit, it should be a number between 1 and and {0}

1
server/conf/routes

@ -17,3 +17,4 @@ GET /stats controllers.StatisticsController.getStatus()
GET /balances controllers.BalancesController.get(offset: Int ?= 0, limit: Int ?= 10, orderBy: String ?= "") GET /balances controllers.BalancesController.get(offset: Int ?= 0, limit: Int ?= 10, orderBy: String ?= "")
GET /masternodes controllers.MasternodesController.get(offset: Int ?= 0, limit: Int ?= 10, orderBy: String ?= "") GET /masternodes controllers.MasternodesController.get(offset: Int ?= 0, limit: Int ?= 10, orderBy: String ?= "")
GET /masternodes/:ip controllers.MasternodesController.getBy(ip: String)

63
server/test/controllers/MasternodesControllerSpec.scala

@ -1,12 +1,14 @@
package controllers package controllers
import com.alexitc.playsonify.PublicErrorRenderer
import com.alexitc.playsonify.core.FutureApplicationResult import com.alexitc.playsonify.core.FutureApplicationResult
import com.xsn.explorer.errors.MasternodeNotFoundError
import com.xsn.explorer.helpers.DummyXSNService import com.xsn.explorer.helpers.DummyXSNService
import com.xsn.explorer.models.{Address, TransactionId}
import com.xsn.explorer.models.rpc.Masternode import com.xsn.explorer.models.rpc.Masternode
import com.xsn.explorer.models.{Address, IPAddress, TransactionId}
import com.xsn.explorer.services.XSNService import com.xsn.explorer.services.XSNService
import controllers.common.MyAPISpec import controllers.common.MyAPISpec
import org.scalactic.{Good, One, Or} import org.scalactic.{Bad, Good}
import play.api.inject.bind import play.api.inject.bind
import play.api.libs.json.JsValue import play.api.libs.json.JsValue
import play.api.test.Helpers._ import play.api.test.Helpers._
@ -35,10 +37,20 @@ class MasternodesControllerSpec extends MyAPISpec {
payee = Address.from("XdNDRAiMUC9KiVRzhCTg9w44jQRdCpCRe3").get) payee = Address.from("XdNDRAiMUC9KiVRzhCTg9w44jQRdCpCRe3").get)
) )
val masternode = masternodes.last
val xsnService = new DummyXSNService { val xsnService = new DummyXSNService {
override def getMasternodes(): FutureApplicationResult[List[Masternode]] = { override def getMasternodes(): FutureApplicationResult[List[Masternode]] = {
Future.successful(Good(masternodes)) Future.successful(Good(masternodes))
} }
override def getMasternode(ipAddress: IPAddress): FutureApplicationResult[Masternode] = {
if (masternode.ip.startsWith(ipAddress.string)) {
Future.successful(Good(masternode))
} else {
Future.successful(Bad(MasternodeNotFoundError).accumulating)
}
}
} }
override val application = guiceApplicationBuilder override val application = guiceApplicationBuilder
@ -48,7 +60,7 @@ class MasternodesControllerSpec extends MyAPISpec {
"GET /masternodes" should { "GET /masternodes" should {
"return the masternodes" in { "return the masternodes" in {
val expected = masternodes.head val expected = masternodes.head
val response = GET("/masternodes?offset=1&limit=10&orderByactiveSeconds:desc") val response = GET("/masternodes?offset=1&limit=10&orderBy=activeSeconds:desc")
status(response) mustEqual OK status(response) mustEqual OK
val json = contentAsJson(response) val json = contentAsJson(response)
@ -67,4 +79,49 @@ class MasternodesControllerSpec extends MyAPISpec {
(item \ "status").as[String] mustEqual expected.status (item \ "status").as[String] mustEqual expected.status
} }
} }
"GET /masternodes/:ip" should {
"return the masternode" in {
val expected = masternode
val response = GET("/masternodes/45.32.148.13")
status(response) mustEqual OK
val json = contentAsJson(response)
(json \ "activeSeconds").as[Long] mustEqual expected.activeSeconds
(json \ "ip").as[String] mustEqual expected.ip
(json \ "lastSeen").as[Long] mustEqual expected.lastSeen
(json \ "payee").as[String] mustEqual expected.payee.string
(json \ "protocol").as[String] mustEqual expected.protocol
(json \ "status").as[String] mustEqual expected.status
(json \ "txid").as[String] mustEqual expected.txid.string
}
"fail on masternode not found" in {
val response = GET("/masternodes/45.32.149.13")
status(response) mustEqual NOT_FOUND
val json = contentAsJson(response)
val errorList = (json \ "errors").as[List[JsValue]]
errorList.size mustEqual 1
val error = errorList.head
(error \ "type").as[String] mustEqual PublicErrorRenderer.FieldValidationErrorType
(error \ "field").as[String] mustEqual "ip"
(error \ "message").as[String].nonEmpty mustEqual true
}
"fail on bad ip format" in {
val response = GET("/masternodes/45.32.149.1333")
status(response) mustEqual BAD_REQUEST
val json = contentAsJson(response)
val errorList = (json \ "errors").as[List[JsValue]]
errorList.size mustEqual 1
val error = errorList.head
(error \ "type").as[String] mustEqual PublicErrorRenderer.FieldValidationErrorType
(error \ "field").as[String] mustEqual "ip"
(error \ "message").as[String].nonEmpty mustEqual true
}
}
} }

Loading…
Cancel
Save