Browse Source

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

This is a piece for #18, it allow us to retrieve the transactions
for the given address.

NOTE: This commit doesn't include tests in order to work in the frontend
      concurrently, tests will be included before the release.
prometheus-integration
Alexis Hernandez 7 years ago
parent
commit
f1bf820b75
  1. 32
      server/app/com/xsn/explorer/services/TransactionService.scala
  2. 10
      server/app/controllers/AddressesController.scala
  3. 3
      server/conf/routes
  4. 81
      server/test/com/xsn/explorer/processors/BlockEventsProcessorSpec.scala

32
server/app/com/xsn/explorer/services/TransactionService.scala

@ -2,17 +2,24 @@ package com.xsn.explorer.services
import javax.inject.Inject import javax.inject.Inject
import com.alexitc.playsonify.core.FutureApplicationResult
import com.alexitc.playsonify.core.FutureOr.Implicits.{FutureListOps, FutureOps, OrOps} import com.alexitc.playsonify.core.FutureOr.Implicits.{FutureListOps, FutureOps, OrOps}
import com.xsn.explorer.errors.{TransactionFormatError, TransactionNotFoundError} import com.alexitc.playsonify.core.{FutureApplicationResult, FuturePaginatedResult}
import com.alexitc.playsonify.models.PaginatedQuery
import com.alexitc.playsonify.validators.PaginatedQueryValidator
import com.xsn.explorer.data.async.TransactionFutureDataHandler
import com.xsn.explorer.errors.{AddressFormatError, TransactionFormatError, TransactionNotFoundError}
import com.xsn.explorer.models._
import com.xsn.explorer.models.rpc.TransactionVIN import com.xsn.explorer.models.rpc.TransactionVIN
import com.xsn.explorer.models.{Transaction, TransactionDetails, TransactionId, TransactionValue}
import org.scalactic.{Bad, Good, One, Or} import org.scalactic.{Bad, Good, One, Or}
import play.api.libs.json.JsValue import play.api.libs.json.JsValue
import scala.concurrent.{ExecutionContext, Future} import scala.concurrent.{ExecutionContext, Future}
class TransactionService @Inject() (xsnService: XSNService)(implicit ec: ExecutionContext) { class TransactionService @Inject() (
paginatedQueryValidator: PaginatedQueryValidator,
xsnService: XSNService,
transactionFutureDataHandler: TransactionFutureDataHandler)(
implicit ec: ExecutionContext) {
def getRawTransaction(txidString: String): FutureApplicationResult[JsValue] = { def getRawTransaction(txidString: String): FutureApplicationResult[JsValue] = {
val result = for { val result = for {
@ -65,6 +72,23 @@ class TransactionService @Inject() (xsnService: XSNService)(implicit ec: Executi
result.toFuture result.toFuture
} }
def getTransactions(
addressString: String,
paginatedQuery: PaginatedQuery): FuturePaginatedResult[TransactionWithValues] = {
val result = for {
address <- {
val maybe = Address.from(addressString)
Or.from(maybe, One(AddressFormatError)).toFutureOr
}
paginatedQuery <- paginatedQueryValidator.validate(paginatedQuery, 100).toFutureOr
transactions <- transactionFutureDataHandler.getBy(address, paginatedQuery).toFutureOr
} yield transactions
result.toFuture
}
private def getTransactionValue(vin: TransactionVIN): FutureApplicationResult[TransactionValue] = { private def getTransactionValue(vin: TransactionVIN): FutureApplicationResult[TransactionValue] = {
val valueMaybe = for { val valueMaybe = for {
value <- vin.value value <- vin.value

10
server/app/controllers/AddressesController.scala

@ -2,15 +2,23 @@ package controllers
import javax.inject.Inject import javax.inject.Inject
import com.xsn.explorer.services.AddressService import com.alexitc.playsonify.models.{Limit, Offset, PaginatedQuery}
import com.xsn.explorer.services.{AddressService, TransactionService}
import controllers.common.{MyJsonController, MyJsonControllerComponents} import controllers.common.{MyJsonController, MyJsonControllerComponents}
class AddressesController @Inject() ( class AddressesController @Inject() (
addressService: AddressService, addressService: AddressService,
transactionService: TransactionService,
cc: MyJsonControllerComponents) cc: MyJsonControllerComponents)
extends MyJsonController(cc) { extends MyJsonController(cc) {
def getDetails(address: String) = publicNoInput { _ => def getDetails(address: String) = publicNoInput { _ =>
addressService.getDetails(address) addressService.getDetails(address)
} }
def getTransactions(address: String, offset: Int, limit: Int) = publicNoInput { _ =>
val paginatedQuery = PaginatedQuery(Offset(offset), Limit(limit))
transactionService.getTransactions(address, paginatedQuery)
}
} }

3
server/conf/routes

@ -8,7 +8,8 @@ GET /health controllers.HealthController.check()
GET /transactions/:txid controllers.TransactionsController.getTransaction(txid: String) GET /transactions/:txid controllers.TransactionsController.getTransaction(txid: String)
GET /transactions/:txid/raw controllers.TransactionsController.getRawTransaction(txid: String) GET /transactions/:txid/raw controllers.TransactionsController.getRawTransaction(txid: String)
GET /addresses/:address controllers.AddressesController.getDetails(address: String) GET /addresses/:address controllers.AddressesController.getDetails(address: String)
GET /addresses/:address/transactions controllers.AddressesController.getTransactions(address: String, offset: Int ?= 0, limit: Int ?= 10)
GET /blocks controllers.BlocksController.getLatestBlocks() GET /blocks controllers.BlocksController.getLatestBlocks()
GET /blocks/:query controllers.BlocksController.getDetails(query: String) GET /blocks/:query controllers.BlocksController.getDetails(query: String)

81
server/test/com/xsn/explorer/processors/BlockEventsProcessorSpec.scala

@ -2,10 +2,11 @@ package com.xsn.explorer.processors
import com.alexitc.playsonify.core.FutureApplicationResult import com.alexitc.playsonify.core.FutureApplicationResult
import com.alexitc.playsonify.models._ import com.alexitc.playsonify.models._
import com.alexitc.playsonify.validators.PaginatedQueryValidator
import com.xsn.explorer.data.anorm._
import com.xsn.explorer.data.anorm.dao.{BalancePostgresDAO, BlockPostgresDAO, StatisticsPostgresDAO, TransactionPostgresDAO} import com.xsn.explorer.data.anorm.dao.{BalancePostgresDAO, BlockPostgresDAO, StatisticsPostgresDAO, TransactionPostgresDAO}
import com.xsn.explorer.data.anorm.interpreters.FieldOrderingSQLInterpreter import com.xsn.explorer.data.anorm.interpreters.FieldOrderingSQLInterpreter
import com.xsn.explorer.data.anorm.{BalancePostgresDataHandler, BlockPostgresDataHandler, DatabasePostgresSeeder, StatisticsPostgresDataHandler} import com.xsn.explorer.data.async.{BlockFutureDataHandler, DatabaseFutureSeeder, TransactionFutureDataHandler}
import com.xsn.explorer.data.async.{BlockFutureDataHandler, DatabaseFutureSeeder}
import com.xsn.explorer.data.common.PostgresDataHandlerSpec import com.xsn.explorer.data.common.PostgresDataHandlerSpec
import com.xsn.explorer.errors.{BlockNotFoundError, TransactionNotFoundError} import com.xsn.explorer.errors.{BlockNotFoundError, TransactionNotFoundError}
import com.xsn.explorer.helpers.{BlockLoader, Executors, FileBasedXSNService} import com.xsn.explorer.helpers.{BlockLoader, Executors, FileBasedXSNService}
@ -13,7 +14,7 @@ import com.xsn.explorer.models.fields.BalanceField
import com.xsn.explorer.models.rpc.{Block, Transaction} import com.xsn.explorer.models.rpc.{Block, Transaction}
import com.xsn.explorer.models.{Blockhash, TransactionId} import com.xsn.explorer.models.{Blockhash, TransactionId}
import com.xsn.explorer.processors.BlockEventsProcessor._ import com.xsn.explorer.processors.BlockEventsProcessor._
import com.xsn.explorer.services.TransactionService import com.xsn.explorer.services.{TransactionService, XSNService}
import org.scalactic.{Bad, Good} import org.scalactic.{Bad, Good}
import org.scalatest.BeforeAndAfter import org.scalatest.BeforeAndAfter
import org.scalatest.concurrent.ScalaFutures import org.scalatest.concurrent.ScalaFutures
@ -23,24 +24,10 @@ import scala.concurrent.Future
class BlockEventsProcessorSpec extends PostgresDataHandlerSpec with ScalaFutures with BeforeAndAfter { class BlockEventsProcessorSpec extends PostgresDataHandlerSpec with ScalaFutures with BeforeAndAfter {
lazy val dataHandler = new BlockPostgresDataHandler(database, new BlockPostgresDAO) lazy val dataHandler = new BlockPostgresDataHandler(database, new BlockPostgresDAO)
lazy val dataSeeder = new DatabasePostgresSeeder( lazy val transactionDataHandler = new TransactionPostgresDataHandler(database, new TransactionPostgresDAO)
database,
new BlockPostgresDAO,
new TransactionPostgresDAO,
new BalancePostgresDAO(new FieldOrderingSQLInterpreter))
lazy val xsnService = new FileBasedXSNService lazy val xsnService = new FileBasedXSNService
lazy val processor = blockEventsProcessor()
lazy val blockOps = new BlockOps(
new DatabaseFutureSeeder(dataSeeder)(Executors.databaseEC),
new BlockFutureDataHandler(dataHandler)(Executors.databaseEC))
lazy val processor = new BlockEventsProcessor(
xsnService,
new TransactionService(xsnService)(Executors.globalEC),
new DatabaseFutureSeeder(dataSeeder)(Executors.databaseEC),
new BlockFutureDataHandler(dataHandler)(Executors.databaseEC),
blockOps)
before { before {
clearDatabase() clearDatabase()
@ -196,14 +183,8 @@ class BlockEventsProcessorSpec extends PostgresDataHandlerSpec with ScalaFutures
} }
} }
val processor = new BlockEventsProcessor( val processor = blockEventsProcessor(xsn = xsnService)
xsnService,
new TransactionService(xsnService)(Executors.globalEC),
new DatabaseFutureSeeder(dataSeeder)(Executors.databaseEC),
new BlockFutureDataHandler(dataHandler)(Executors.databaseEC),
blockOps)
List(block1, block2) List(block1, block2)
.map(_.hash) .map(_.hash)
.map(processor.processBlock) .map(processor.processBlock)
@ -236,13 +217,7 @@ class BlockEventsProcessorSpec extends PostgresDataHandlerSpec with ScalaFutures
} }
} }
val processor = new BlockEventsProcessor( val processor = blockEventsProcessor(xsn = xsnService)
xsnService,
new TransactionService(xsnService)(Executors.globalEC),
new DatabaseFutureSeeder(dataSeeder)(Executors.databaseEC),
new BlockFutureDataHandler(dataHandler)(Executors.databaseEC),
blockOps)
List(block1, block2) List(block1, block2)
.map(_.hash) .map(_.hash)
@ -278,12 +253,7 @@ class BlockEventsProcessorSpec extends PostgresDataHandlerSpec with ScalaFutures
} }
} }
val processor = new BlockEventsProcessor( val processor = blockEventsProcessor(xsn = xsnService)
xsnService,
new TransactionService(xsnService)(Executors.globalEC),
new DatabaseFutureSeeder(dataSeeder)(Executors.databaseEC),
new BlockFutureDataHandler(dataHandler)(Executors.databaseEC),
blockOps)
List(block1, block2) List(block1, block2)
.map(_.hash) .map(_.hash)
@ -317,12 +287,7 @@ class BlockEventsProcessorSpec extends PostgresDataHandlerSpec with ScalaFutures
} }
} }
val processor = new BlockEventsProcessor( val processor = blockEventsProcessor(xsn = xsnService)
xsnService,
new TransactionService(xsnService)(Executors.globalEC),
new DatabaseFutureSeeder(dataSeeder)(Executors.databaseEC),
new BlockFutureDataHandler(dataHandler)(Executors.databaseEC),
blockOps)
List(block1, rareBlock3) List(block1, rareBlock3)
.map(_.hash) .map(_.hash)
@ -351,4 +316,28 @@ class BlockEventsProcessorSpec extends PostgresDataHandlerSpec with ScalaFutures
_root_.anorm.SQL("""SELECT COUNT(*) FROM blocks""").as(_root_.anorm.SqlParser.scalar[Int].single) _root_.anorm.SQL("""SELECT COUNT(*) FROM blocks""").as(_root_.anorm.SqlParser.scalar[Int].single)
} }
} }
private def blockEventsProcessor(xsn: XSNService = xsnService) = {
val dataSeeder = new DatabasePostgresSeeder(
database,
new BlockPostgresDAO,
new TransactionPostgresDAO,
new BalancePostgresDAO(new FieldOrderingSQLInterpreter))
val blockOps = new BlockOps(
new DatabaseFutureSeeder(dataSeeder)(Executors.databaseEC),
new BlockFutureDataHandler(dataHandler)(Executors.databaseEC))
val transactionService = new TransactionService(
new PaginatedQueryValidator,
xsn,
new TransactionFutureDataHandler(transactionDataHandler)(Executors.databaseEC))(Executors.globalEC)
new BlockEventsProcessor(
xsn,
transactionService,
new DatabaseFutureSeeder(dataSeeder)(Executors.databaseEC),
new BlockFutureDataHandler(dataHandler)(Executors.databaseEC),
blockOps)
}
} }

Loading…
Cancel
Save