From e9eb79b813bf489b8de9c5a915c686c21a62c875 Mon Sep 17 00:00:00 2001 From: Alexis Hernandez Date: Sat, 12 May 2018 00:04:23 -0500 Subject: [PATCH] server: Hide 0 balances from the balances API It is unnecessary to be listing 0 balances on the richest addresses. --- .../explorer/data/BalanceDataHandler.scala | 2 + .../anorm/BalancePostgresDataHandler.scala | 13 +++++- .../data/anorm/dao/BalancePostgresDAO.scala | 41 ++++++++++++++++++- .../data/async/BalanceFutureDataHandler.scala | 7 ++++ .../explorer/services/BalanceService.scala | 2 +- .../data/BalancePostgresDataHandlerSpec.scala | 26 ++++++++++++ .../controllers/BalancesControllerSpec.scala | 9 ++-- 7 files changed, 94 insertions(+), 6 deletions(-) diff --git a/server/app/com/xsn/explorer/data/BalanceDataHandler.scala b/server/app/com/xsn/explorer/data/BalanceDataHandler.scala index e3ed50f..f148f2b 100644 --- a/server/app/com/xsn/explorer/data/BalanceDataHandler.scala +++ b/server/app/com/xsn/explorer/data/BalanceDataHandler.scala @@ -12,6 +12,8 @@ trait BalanceDataHandler[F[_]] { def upsert(balance: Balance): F[Balance] def get(query: PaginatedQuery, ordering: FieldOrdering[BalanceField]): F[PaginatedResult[Balance]] + + def getNonZeroBalances(query: PaginatedQuery, ordering: FieldOrdering[BalanceField]): F[PaginatedResult[Balance]] } trait BalanceBlockingDataHandler extends BalanceDataHandler[ApplicationResult] diff --git a/server/app/com/xsn/explorer/data/anorm/BalancePostgresDataHandler.scala b/server/app/com/xsn/explorer/data/anorm/BalancePostgresDataHandler.scala index c343582..8f35edf 100644 --- a/server/app/com/xsn/explorer/data/anorm/BalancePostgresDataHandler.scala +++ b/server/app/com/xsn/explorer/data/anorm/BalancePostgresDataHandler.scala @@ -29,7 +29,18 @@ class BalancePostgresDataHandler @Inject() ( ordering: FieldOrdering[BalanceField]): ApplicationResult[PaginatedResult[Balance]] = withConnection { implicit conn => val balances = balancePostgresDAO.get(query, ordering) - val total = balancePostgresDAO.countRichest + val total = balancePostgresDAO.count + val result = PaginatedResult(query.offset, query.limit, total, balances) + + Good(result) + } + + override def getNonZeroBalances( + query: PaginatedQuery, + ordering: FieldOrdering[BalanceField]): ApplicationResult[PaginatedResult[Balance]] = withConnection { implicit conn => + + val balances = balancePostgresDAO.getNonZeroBalances(query, ordering) + val total = balancePostgresDAO.countNonZeroBalances val result = PaginatedResult(query.offset, query.limit, total, balances) Good(result) diff --git a/server/app/com/xsn/explorer/data/anorm/dao/BalancePostgresDAO.scala b/server/app/com/xsn/explorer/data/anorm/dao/BalancePostgresDAO.scala index d75609b..4e4ad5c 100644 --- a/server/app/com/xsn/explorer/data/anorm/dao/BalancePostgresDAO.scala +++ b/server/app/com/xsn/explorer/data/anorm/dao/BalancePostgresDAO.scala @@ -55,7 +55,31 @@ class BalancePostgresDAO @Inject() (fieldOrderingSQLInterpreter: FieldOrderingSQ ).as(parseBalance.*).flatten } - def countRichest(implicit conn: Connection): Count = { + def getNonZeroBalances( + query: PaginatedQuery, + ordering: FieldOrdering[BalanceField])( + implicit conn: Connection): List[Balance] = { + + val orderBy = fieldOrderingSQLInterpreter.toOrderByClause(ordering) + SQL( + s""" + |SELECT address, received, spent + |FROM balances + |WHERE address NOT IN ( + | SELECT address + | FROM hidden_addresses) AND + | (received - spent) > 0 + |$orderBy + |OFFSET {offset} + |LIMIT {limit} + """.stripMargin + ).on( + 'offset -> query.offset.int, + 'limit -> query.limit.int + ).as(parseBalance.*).flatten + } + + def count(implicit conn: Connection): Count = { val result = SQL( """ |SELECT COUNT(*) @@ -69,4 +93,19 @@ class BalancePostgresDAO @Inject() (fieldOrderingSQLInterpreter: FieldOrderingSQ Count(result) } + + def countNonZeroBalances(implicit conn: Connection): Count = { + val result = SQL( + """ + |SELECT COUNT(*) + |FROM balances + |WHERE address NOT IN ( + | SELECT address + | FROM hidden_addresses) AND + | (received - spent) > 0 + """.stripMargin + ).as(SqlParser.scalar[Int].single) + + Count(result) + } } diff --git a/server/app/com/xsn/explorer/data/async/BalanceFutureDataHandler.scala b/server/app/com/xsn/explorer/data/async/BalanceFutureDataHandler.scala index f15ae5e..684b69e 100644 --- a/server/app/com/xsn/explorer/data/async/BalanceFutureDataHandler.scala +++ b/server/app/com/xsn/explorer/data/async/BalanceFutureDataHandler.scala @@ -23,4 +23,11 @@ class BalanceFutureDataHandler @Inject() ( override def get(query: PaginatedQuery, ordering: FieldOrdering[BalanceField]): FuturePaginatedResult[Balance] = Future { blockingDataHandler.get(query, ordering) } + + override def getNonZeroBalances( + query: PaginatedQuery, + ordering: FieldOrdering[BalanceField]): FuturePaginatedResult[Balance] = Future { + + blockingDataHandler.getNonZeroBalances(query, ordering) + } } diff --git a/server/app/com/xsn/explorer/services/BalanceService.scala b/server/app/com/xsn/explorer/services/BalanceService.scala index 90d0f46..d8f5981 100644 --- a/server/app/com/xsn/explorer/services/BalanceService.scala +++ b/server/app/com/xsn/explorer/services/BalanceService.scala @@ -22,7 +22,7 @@ class BalanceService @Inject() ( val result = for { validatedQuery <- paginatedQueryValidator.validate(paginatedQuery, 100).toFutureOr ordering <- balanceOrderingParser.from(orderingQuery).toFutureOr - balances <- balanceFutureDataHandler.get(validatedQuery, ordering).toFutureOr + balances <- balanceFutureDataHandler.getNonZeroBalances(validatedQuery, ordering).toFutureOr } yield balances result.toFuture diff --git a/server/test/com/xsn/explorer/data/BalancePostgresDataHandlerSpec.scala b/server/test/com/xsn/explorer/data/BalancePostgresDataHandlerSpec.scala index ef97930..a9743c6 100644 --- a/server/test/com/xsn/explorer/data/BalancePostgresDataHandlerSpec.scala +++ b/server/test/com/xsn/explorer/data/BalancePostgresDataHandlerSpec.scala @@ -89,6 +89,32 @@ class BalancePostgresDataHandlerSpec extends PostgresDataHandlerSpec { } } + "getNonZeroBalances" should { + val balances = List( + Balance( + address = DataHelper.createAddress("XiHW7SR56uPHeXKwcpeVsE4nUfkHv5RqE3"), + received = BigDecimal("0"), + spent = BigDecimal("0")) + ).sortBy(_.available).reverse + + def prepare() = { + clearDatabase() + + balances + .map(dataHandler.upsert) + .foreach(_.isGood mustEqual true) + } + "ignore addresses with balance = 0" in { + prepare() + val query = PaginatedQuery(Offset(0), Limit(1)) + val expected = List.empty[Balance] + + val result = dataHandler.getNonZeroBalances(query, defaultOrdering) + result.map(_.data) mustEqual Good(expected) + result.get.total.int mustEqual balances.size - 1 + } + } + private def combine(balances: Balance*): Balance = { balances.reduce { (a, b) => Balance(a.address, a.received + b.received, a.spent + b.spent) diff --git a/server/test/controllers/BalancesControllerSpec.scala b/server/test/controllers/BalancesControllerSpec.scala index 6cf3206..7051158 100644 --- a/server/test/controllers/BalancesControllerSpec.scala +++ b/server/test/controllers/BalancesControllerSpec.scala @@ -41,12 +41,15 @@ class BalancesControllerSpec extends MyAPISpec { override def upsert(balance: Balance): ApplicationResult[Balance] = ??? - override def get(query: PaginatedQuery, ordering: FieldOrdering[BalanceField]): ApplicationResult[PaginatedResult[Balance]] = { - val list = balances.drop(query.offset.int).take(query.limit.int) + override def get(query: PaginatedQuery, ordering: FieldOrdering[BalanceField]): ApplicationResult[PaginatedResult[Balance]] = ??? + + override def getNonZeroBalances(query: PaginatedQuery, ordering: FieldOrdering[BalanceField]): ApplicationResult[PaginatedResult[Balance]] = { + val filtered = balances.filter(_.available > 0) + val list = filtered.drop(query.offset.int).take(query.limit.int) val result = PaginatedResult( offset = query.offset, limit = query.limit, - total = Count(balances.size), + total = Count(filtered.size), data = list) Good(result)