Browse Source
When running the task, we replace a block when its height already exists, this should help on the fix for #11 and #12.scalafmt-draft
3 changed files with 121 additions and 6 deletions
@ -0,0 +1,65 @@ |
|||
package com.xsn.explorer.processors |
|||
|
|||
import javax.inject.Inject |
|||
|
|||
import com.alexitc.playsonify.core.FutureApplicationResult |
|||
import com.alexitc.playsonify.core.FutureOr.Implicits.FutureOps |
|||
import com.xsn.explorer.data.DatabaseSeeder |
|||
import com.xsn.explorer.data.async.{BlockFutureDataHandler, DatabaseFutureSeeder} |
|||
import com.xsn.explorer.errors.PostgresForeignKeyViolationError |
|||
import com.xsn.explorer.models.Transaction |
|||
import com.xsn.explorer.models.rpc.Block |
|||
import org.scalactic.{Bad, Good, One} |
|||
|
|||
import scala.concurrent.{ExecutionContext, Future} |
|||
|
|||
/** |
|||
* BlockOps is contains useful operations used for seeding the database, while these methods might be adequate to be on |
|||
* the [[com.xsn.explorer.services.BlockService]] class, they were added here to avoid growing that service, |
|||
* in any case, these methods are used by the seeding process. |
|||
*/ |
|||
class BlockOps @Inject() ( |
|||
databaseSeeder: DatabaseFutureSeeder, |
|||
blockDataHandler: BlockFutureDataHandler)( |
|||
implicit val ec: ExecutionContext) { |
|||
|
|||
import BlockOps._ |
|||
|
|||
/** |
|||
* Creates a new block and in case of a conflict due to repeated height, the old block is replaced. |
|||
*/ |
|||
def createBlock(block: Block, transactions: List[Transaction]): FutureApplicationResult[Result] = { |
|||
val command = DatabaseSeeder.CreateBlockCommand(block, transactions) |
|||
databaseSeeder |
|||
.newBlock(command) |
|||
.flatMap { |
|||
case Good(_) => Future.successful(Good(Result.BlockCreated)) |
|||
case Bad(One(PostgresForeignKeyViolationError("height", _))) => onRepeatedBlockHeight(block, transactions) |
|||
case Bad(errors) => Future.successful(Bad(errors)) |
|||
} |
|||
} |
|||
|
|||
private def onRepeatedBlockHeight(newBlock: Block, newTransactions: List[Transaction]): FutureApplicationResult[Result] = { |
|||
val result = for { |
|||
orphanBlock <- blockDataHandler.getBy(newBlock.height).toFutureOr |
|||
|
|||
replaceCommand = DatabaseSeeder.ReplaceBlockCommand( |
|||
orphanBlock = orphanBlock, |
|||
newBlock = newBlock, |
|||
newTransactions = newTransactions) |
|||
|
|||
_ <- databaseSeeder.replaceBlock(replaceCommand).toFutureOr |
|||
} yield Result.BlockReplacedByHeight |
|||
|
|||
result.toFuture |
|||
} |
|||
} |
|||
|
|||
object BlockOps { |
|||
|
|||
sealed trait Result |
|||
object Result { |
|||
case object BlockCreated extends Result |
|||
case object BlockReplacedByHeight extends Result |
|||
} |
|||
} |
@ -0,0 +1,51 @@ |
|||
package com.xsn.explorer.processors |
|||
|
|||
import com.xsn.explorer.data.anorm.dao.{BalancePostgresDAO, BlockPostgresDAO, TransactionPostgresDAO} |
|||
import com.xsn.explorer.data.anorm.interpreters.FieldOrderingSQLInterpreter |
|||
import com.xsn.explorer.data.anorm.{BlockPostgresDataHandler, DatabasePostgresSeeder} |
|||
import com.xsn.explorer.data.async.{BlockFutureDataHandler, DatabaseFutureSeeder} |
|||
import com.xsn.explorer.data.common.PostgresDataHandlerSpec |
|||
import com.xsn.explorer.helpers.{BlockLoader, Executors, FileBasedXSNService} |
|||
import org.scalactic.Good |
|||
import org.scalatest.BeforeAndAfter |
|||
import org.scalatest.concurrent.ScalaFutures |
|||
|
|||
class BlockOpsSpec extends PostgresDataHandlerSpec with ScalaFutures with BeforeAndAfter { |
|||
|
|||
before { |
|||
clearDatabase() |
|||
} |
|||
|
|||
lazy val dataHandler = new BlockPostgresDataHandler(database, new BlockPostgresDAO) |
|||
lazy val dataSeeder = new DatabasePostgresSeeder( |
|||
database, |
|||
new BlockPostgresDAO, |
|||
new TransactionPostgresDAO, |
|||
new BalancePostgresDAO(new FieldOrderingSQLInterpreter)) |
|||
|
|||
lazy val xsnService = new FileBasedXSNService |
|||
|
|||
lazy val blockOps = new BlockOps( |
|||
new DatabaseFutureSeeder(dataSeeder)(Executors.databaseEC), |
|||
new BlockFutureDataHandler(dataHandler)(Executors.databaseEC)) |
|||
|
|||
|
|||
val block1 = BlockLoader.get("000003fb382f6892ae96594b81aa916a8923c70701de4e7054aac556c7271ef7") |
|||
val block2 = BlockLoader.get("000004645e2717b556682e3c642a4c6e473bf25c653ff8e8c114a3006040ffb8") |
|||
|
|||
"createBlock" should { |
|||
"create a new block" in { |
|||
whenReady(blockOps.createBlock(block1, List.empty)) { result => |
|||
result mustEqual Good(BlockOps.Result.BlockCreated) |
|||
} |
|||
} |
|||
|
|||
"replace a block if the height already exist" in { |
|||
whenReady(blockOps.createBlock(block1, List.empty)) { _.isGood mustEqual true } |
|||
|
|||
whenReady(blockOps.createBlock(block2.copy(height = block1.height), List.empty)) { result => |
|||
result mustEqual Good(BlockOps.Result.BlockReplacedByHeight) |
|||
} |
|||
} |
|||
} |
|||
} |
Loading…
Reference in new issue