Browse Source

server: Allow to reverse the block headers ordering

master
Alexis Hernandez 6 years ago
parent
commit
be72834662
  1. 4
      server/app/com/xsn/explorer/data/BlockDataHandler.scala
  2. 7
      server/app/com/xsn/explorer/data/anorm/BlockPostgresDataHandler.scala
  3. 27
      server/app/com/xsn/explorer/data/anorm/dao/BlockPostgresDAO.scala
  4. 10
      server/app/com/xsn/explorer/data/async/BlockFutureDataHandler.scala
  5. 13
      server/app/com/xsn/explorer/services/BlockService.scala
  6. 4
      server/app/controllers/BlocksController.scala
  7. 2
      server/conf/routes
  8. 3
      server/test/com/xsn/explorer/services/LedgerSynchronizerServiceSpec.scala

4
server/app/com/xsn/explorer/data/BlockDataHandler.scala

@ -1,7 +1,7 @@
package com.xsn.explorer.data package com.xsn.explorer.data
import com.alexitc.playsonify.core.ApplicationResult import com.alexitc.playsonify.core.ApplicationResult
import com.alexitc.playsonify.models.ordering.FieldOrdering import com.alexitc.playsonify.models.ordering.{FieldOrdering, OrderingCondition}
import com.alexitc.playsonify.models.pagination.{Limit, PaginatedQuery, PaginatedResult} import com.alexitc.playsonify.models.pagination.{Limit, PaginatedQuery, PaginatedResult}
import com.xsn.explorer.models.fields.BlockField import com.xsn.explorer.models.fields.BlockField
import com.xsn.explorer.models.persisted.{Block, BlockHeader} import com.xsn.explorer.models.persisted.{Block, BlockHeader}
@ -25,7 +25,7 @@ trait BlockDataHandler[F[_]] {
def getFirstBlock(): F[Block] def getFirstBlock(): F[Block]
def getHeaders(limit: Limit, lastSeenHash: Option[Blockhash]): F[List[BlockHeader]] def getHeaders(limit: Limit, orderingCondition: OrderingCondition, lastSeenHash: Option[Blockhash]): F[List[BlockHeader]]
} }
trait BlockBlockingDataHandler extends BlockDataHandler[ApplicationResult] trait BlockBlockingDataHandler extends BlockDataHandler[ApplicationResult]

7
server/app/com/xsn/explorer/data/anorm/BlockPostgresDataHandler.scala

@ -1,7 +1,7 @@
package com.xsn.explorer.data.anorm package com.xsn.explorer.data.anorm
import com.alexitc.playsonify.core.ApplicationResult import com.alexitc.playsonify.core.ApplicationResult
import com.alexitc.playsonify.models.ordering.FieldOrdering import com.alexitc.playsonify.models.ordering.{FieldOrdering, OrderingCondition}
import com.alexitc.playsonify.models.pagination import com.alexitc.playsonify.models.pagination
import com.alexitc.playsonify.models.pagination.{PaginatedQuery, PaginatedResult} import com.alexitc.playsonify.models.pagination.{PaginatedQuery, PaginatedResult}
import com.xsn.explorer.data.BlockBlockingDataHandler import com.xsn.explorer.data.BlockBlockingDataHandler
@ -58,13 +58,14 @@ class BlockPostgresDataHandler @Inject() (
override def getHeaders( override def getHeaders(
limit: pagination.Limit, limit: pagination.Limit,
orderingCondition: OrderingCondition,
lastSeenHash: Option[Blockhash]): ApplicationResult[List[BlockHeader]] = withConnection { implicit conn => lastSeenHash: Option[Blockhash]): ApplicationResult[List[BlockHeader]] = withConnection { implicit conn =>
val result = lastSeenHash val result = lastSeenHash
.map { hash => .map { hash =>
blockPostgresDAO.getHeaders(hash, limit) blockPostgresDAO.getHeaders(hash, limit, orderingCondition)
} }
.getOrElse { blockPostgresDAO.getHeaders(limit) } .getOrElse { blockPostgresDAO.getHeaders(limit, orderingCondition) }
Good(result) Good(result)
} }

27
server/app/com/xsn/explorer/data/anorm/dao/BlockPostgresDAO.scala

@ -151,12 +151,14 @@ class BlockPostgresDAO @Inject() (fieldOrderingSQLInterpreter: FieldOrderingSQLI
getBy(query, ordering).headOption getBy(query, ordering).headOption
} }
def getHeaders(limit: Limit)(implicit conn: Connection): List[BlockHeader] = { def getHeaders(limit: Limit, orderingCondition: OrderingCondition)(implicit conn: Connection): List[BlockHeader] = {
val order = toSQL(orderingCondition)
SQL( SQL(
""" s"""
|SELECT blockhash, previous_blockhash, merkle_root, height, time |SELECT blockhash, previous_blockhash, merkle_root, height, time
|FROM blocks |FROM blocks
|ORDER BY height |ORDER BY height $order
|LIMIT {limit} |LIMIT {limit}
""".stripMargin """.stripMargin
).on( ).on(
@ -164,9 +166,15 @@ class BlockPostgresDAO @Inject() (fieldOrderingSQLInterpreter: FieldOrderingSQLI
).as(parseHeader.*) ).as(parseHeader.*)
} }
def getHeaders(lastSeenHash: Blockhash, limit: Limit)(implicit conn: Connection): List[BlockHeader] = { def getHeaders(lastSeenHash: Blockhash, limit: Limit, orderingCondition: OrderingCondition)(implicit conn: Connection): List[BlockHeader] = {
val order = toSQL(orderingCondition)
val comparator = orderingCondition match {
case OrderingCondition.DescendingOrder => "<"
case OrderingCondition.AscendingOrder => ">"
}
SQL( SQL(
""" s"""
|WITH CTE AS ( |WITH CTE AS (
| SELECT height as lastSeenHeight | SELECT height as lastSeenHeight
| FROM blocks | FROM blocks
@ -174,8 +182,8 @@ class BlockPostgresDAO @Inject() (fieldOrderingSQLInterpreter: FieldOrderingSQLI
|) |)
|SELECT b.blockhash, b.previous_blockhash, b.merkle_root, b.height, b.time |SELECT b.blockhash, b.previous_blockhash, b.merkle_root, b.height, b.time
|FROM CTE CROSS JOIN blocks b |FROM CTE CROSS JOIN blocks b
|WHERE b.height > lastSeenHeight |WHERE b.height $comparator lastSeenHeight
|ORDER BY height |ORDER BY height $order
|LIMIT {limit} |LIMIT {limit}
""".stripMargin """.stripMargin
).on( ).on(
@ -183,4 +191,9 @@ class BlockPostgresDAO @Inject() (fieldOrderingSQLInterpreter: FieldOrderingSQLI
'limit -> limit.int 'limit -> limit.int
).as(parseHeader.*) ).as(parseHeader.*)
} }
private def toSQL(condition: OrderingCondition): String = condition match {
case OrderingCondition.AscendingOrder => "ASC"
case OrderingCondition.DescendingOrder => "DESC"
}
} }

10
server/app/com/xsn/explorer/data/async/BlockFutureDataHandler.scala

@ -1,7 +1,7 @@
package com.xsn.explorer.data.async package com.xsn.explorer.data.async
import com.alexitc.playsonify.core.{FutureApplicationResult, FuturePaginatedResult} import com.alexitc.playsonify.core.{FutureApplicationResult, FuturePaginatedResult}
import com.alexitc.playsonify.models.ordering.FieldOrdering import com.alexitc.playsonify.models.ordering.{FieldOrdering, OrderingCondition}
import com.alexitc.playsonify.models.pagination import com.alexitc.playsonify.models.pagination
import com.alexitc.playsonify.models.pagination.PaginatedQuery import com.alexitc.playsonify.models.pagination.PaginatedQuery
import com.xsn.explorer.data.{BlockBlockingDataHandler, BlockDataHandler} import com.xsn.explorer.data.{BlockBlockingDataHandler, BlockDataHandler}
@ -42,7 +42,11 @@ class BlockFutureDataHandler @Inject() (
blockBlockingDataHandler.getFirstBlock() blockBlockingDataHandler.getFirstBlock()
} }
override def getHeaders(limit: pagination.Limit, lastSeenHash: Option[Blockhash]): FutureApplicationResult[List[BlockHeader]] = Future { override def getHeaders(
blockBlockingDataHandler.getHeaders(limit, lastSeenHash) limit: pagination.Limit,
orderingCondition: OrderingCondition,
lastSeenHash: Option[Blockhash]): FutureApplicationResult[List[BlockHeader]] = Future {
blockBlockingDataHandler.getHeaders(limit, orderingCondition, lastSeenHash)
} }
} }

13
server/app/com/xsn/explorer/services/BlockService.scala

@ -9,6 +9,7 @@ import com.xsn.explorer.errors.{BlockRewardsNotFoundError, BlockhashFormatError}
import com.xsn.explorer.models._ import com.xsn.explorer.models._
import com.xsn.explorer.models.rpc.{Block, TransactionVIN} import com.xsn.explorer.models.rpc.{Block, TransactionVIN}
import com.xsn.explorer.models.values.{Blockhash, Height} import com.xsn.explorer.models.values.{Blockhash, Height}
import com.xsn.explorer.parsers.OrderingConditionParser
import com.xsn.explorer.services.logic.{BlockLogic, TransactionLogic} import com.xsn.explorer.services.logic.{BlockLogic, TransactionLogic}
import com.xsn.explorer.util.Extensions.FutureOrExt import com.xsn.explorer.util.Extensions.FutureOrExt
import javax.inject.Inject import javax.inject.Inject
@ -22,12 +23,17 @@ class BlockService @Inject() (
blockDataHandler: BlockFutureDataHandler, blockDataHandler: BlockFutureDataHandler,
paginatedQueryValidator: PaginatedQueryValidator, paginatedQueryValidator: PaginatedQueryValidator,
blockLogic: BlockLogic, blockLogic: BlockLogic,
transactionLogic: TransactionLogic)( transactionLogic: TransactionLogic,
orderingConditionParser: OrderingConditionParser)(
implicit ec: ExecutionContext) { implicit ec: ExecutionContext) {
private val maxHeadersPerQuery = 1000 private val maxHeadersPerQuery = 1000
def getBlockHeaders(limit: Limit, lastSeenHashString: Option[String]): FutureApplicationResult[WrappedResult[List[persisted.BlockHeader]]] = { def getBlockHeaders(
limit: Limit,
lastSeenHashString: Option[String],
orderingConditionString: String): FutureApplicationResult[WrappedResult[List[persisted.BlockHeader]]] = {
val result = for { val result = for {
lastSeenHash <- { lastSeenHash <- {
lastSeenHashString lastSeenHashString
@ -38,8 +44,9 @@ class BlockService @Inject() (
} }
_ <- paginatedQueryValidator.validate(PaginatedQuery(Offset(0), limit), maxHeadersPerQuery).toFutureOr _ <- paginatedQueryValidator.validate(PaginatedQuery(Offset(0), limit), maxHeadersPerQuery).toFutureOr
orderingCondition <- orderingConditionParser.parseReuslt(orderingConditionString).toFutureOr
headers <- blockDataHandler.getHeaders(limit, lastSeenHash).toFutureOr headers <- blockDataHandler.getHeaders(limit, orderingCondition, lastSeenHash).toFutureOr
} yield WrappedResult(headers) } yield WrappedResult(headers)
result.toFuture result.toFuture

4
server/app/controllers/BlocksController.scala

@ -24,8 +24,8 @@ class BlocksController @Inject() (
blockService.getLatestBlocks() blockService.getLatestBlocks()
} }
def getBlockHeaders(lastSeenHash: Option[String], limit: Int) = public { _ => def getBlockHeaders(lastSeenHash: Option[String], limit: Int, orderingCondition: String) = public { _ =>
blockService.getBlockHeaders(Limit(limit), lastSeenHash) blockService.getBlockHeaders(Limit(limit), lastSeenHash, orderingCondition)
} }
/** /**

2
server/conf/routes

@ -16,7 +16,7 @@ GET /v2/addresses/:address/transactions controllers.AddressesContro
GET /addresses/:address/utxos controllers.AddressesController.getUnspentOutputs(address: String) GET /addresses/:address/utxos controllers.AddressesController.getUnspentOutputs(address: String)
GET /blocks controllers.BlocksController.getLatestBlocks() GET /blocks controllers.BlocksController.getLatestBlocks()
GET /blocks/headers controllers.BlocksController.getBlockHeaders(lastSeenHash: Option[String], limit: Int ?= 10) GET /blocks/headers controllers.BlocksController.getBlockHeaders(lastSeenHash: Option[String], limit: Int ?= 10, order: String ?= "asc")
GET /blocks/:query controllers.BlocksController.getDetails(query: String) GET /blocks/:query controllers.BlocksController.getDetails(query: String)
GET /blocks/:query/raw controllers.BlocksController.getRawBlock(query: String) GET /blocks/:query/raw controllers.BlocksController.getRawBlock(query: String)

3
server/test/com/xsn/explorer/services/LedgerSynchronizerServiceSpec.scala

@ -221,7 +221,8 @@ class LedgerSynchronizerServiceSpec extends PostgresDataHandlerSpec with BeforeA
new BlockFutureDataHandler(blockDataHandler)(Executors.databaseEC), new BlockFutureDataHandler(blockDataHandler)(Executors.databaseEC),
new PaginatedQueryValidator, new PaginatedQueryValidator,
new BlockLogic, new BlockLogic,
new TransactionLogic) new TransactionLogic,
new OrderingConditionParser)
val transactionRPCService = new TransactionRPCService(xsnService) val transactionRPCService = new TransactionRPCService(xsnService)
new LedgerSynchronizerService( new LedgerSynchronizerService(
xsnService, xsnService,

Loading…
Cancel
Save