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
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.xsn.explorer.models.fields.BlockField
import com.xsn.explorer.models.persisted.{Block, BlockHeader}
@ -25,7 +25,7 @@ trait BlockDataHandler[F[_]] {
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]

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

@ -1,7 +1,7 @@
package com.xsn.explorer.data.anorm
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.{PaginatedQuery, PaginatedResult}
import com.xsn.explorer.data.BlockBlockingDataHandler
@ -58,13 +58,14 @@ class BlockPostgresDataHandler @Inject() (
override def getHeaders(
limit: pagination.Limit,
orderingCondition: OrderingCondition,
lastSeenHash: Option[Blockhash]): ApplicationResult[List[BlockHeader]] = withConnection { implicit conn =>
val result = lastSeenHash
.map { hash =>
blockPostgresDAO.getHeaders(hash, limit)
blockPostgresDAO.getHeaders(hash, limit, orderingCondition)
}
.getOrElse { blockPostgresDAO.getHeaders(limit) }
.getOrElse { blockPostgresDAO.getHeaders(limit, orderingCondition) }
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
}
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(
"""
s"""
|SELECT blockhash, previous_blockhash, merkle_root, height, time
|FROM blocks
|ORDER BY height
|ORDER BY height $order
|LIMIT {limit}
""".stripMargin
).on(
@ -164,9 +166,15 @@ class BlockPostgresDAO @Inject() (fieldOrderingSQLInterpreter: FieldOrderingSQLI
).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(
"""
s"""
|WITH CTE AS (
| SELECT height as lastSeenHeight
| FROM blocks
@ -174,8 +182,8 @@ class BlockPostgresDAO @Inject() (fieldOrderingSQLInterpreter: FieldOrderingSQLI
|)
|SELECT b.blockhash, b.previous_blockhash, b.merkle_root, b.height, b.time
|FROM CTE CROSS JOIN blocks b
|WHERE b.height > lastSeenHeight
|ORDER BY height
|WHERE b.height $comparator lastSeenHeight
|ORDER BY height $order
|LIMIT {limit}
""".stripMargin
).on(
@ -183,4 +191,9 @@ class BlockPostgresDAO @Inject() (fieldOrderingSQLInterpreter: FieldOrderingSQLI
'limit -> limit.int
).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
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.PaginatedQuery
import com.xsn.explorer.data.{BlockBlockingDataHandler, BlockDataHandler}
@ -42,7 +42,11 @@ class BlockFutureDataHandler @Inject() (
blockBlockingDataHandler.getFirstBlock()
}
override def getHeaders(limit: pagination.Limit, lastSeenHash: Option[Blockhash]): FutureApplicationResult[List[BlockHeader]] = Future {
blockBlockingDataHandler.getHeaders(limit, lastSeenHash)
override def getHeaders(
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.rpc.{Block, TransactionVIN}
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.util.Extensions.FutureOrExt
import javax.inject.Inject
@ -22,12 +23,17 @@ class BlockService @Inject() (
blockDataHandler: BlockFutureDataHandler,
paginatedQueryValidator: PaginatedQueryValidator,
blockLogic: BlockLogic,
transactionLogic: TransactionLogic)(
transactionLogic: TransactionLogic,
orderingConditionParser: OrderingConditionParser)(
implicit ec: ExecutionContext) {
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 {
lastSeenHash <- {
lastSeenHashString
@ -38,8 +44,9 @@ class BlockService @Inject() (
}
_ <- 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)
result.toFuture

4
server/app/controllers/BlocksController.scala

@ -24,8 +24,8 @@ class BlocksController @Inject() (
blockService.getLatestBlocks()
}
def getBlockHeaders(lastSeenHash: Option[String], limit: Int) = public { _ =>
blockService.getBlockHeaders(Limit(limit), lastSeenHash)
def getBlockHeaders(lastSeenHash: Option[String], limit: Int, orderingCondition: String) = public { _ =>
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 /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/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 PaginatedQueryValidator,
new BlockLogic,
new TransactionLogic)
new TransactionLogic,
new OrderingConditionParser)
val transactionRPCService = new TransactionRPCService(xsnService)
new LedgerSynchronizerService(
xsnService,

Loading…
Cancel
Save