Skip to content

Commit

Permalink
Assets added to search.
Browse files Browse the repository at this point in the history
  • Loading branch information
oskin1 committed Dec 30, 2020
1 parent 06c6f69 commit 2bb1dcb
Show file tree
Hide file tree
Showing 10 changed files with 107 additions and 50 deletions.
2 changes: 1 addition & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = "2.6.3"
version = "2.7.5"
style = defaultWithAlign

align.openParenCallSite = false
Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ lazy val commonSettings = Seq(
scalacOptions ++= commonScalacOptions,
scalaVersion := "2.12.12",
organization := "org.ergoplatform",
version := "5.0.0",
version := "5.1.0",
resolvers += Resolver.sonatypeRepo("public"),
resolvers += Resolver.sonatypeRepo("snapshots"),
libraryDependencies ++= dependencies.Testing ++ dependencies.CompilerPlugins,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package org.ergoplatform.explorer.http.api.models
import io.circe.Codec
import io.circe.generic.semiauto.deriveCodec
import org.ergoplatform.explorer.TokenId
import org.ergoplatform.explorer.db.models.UAsset
import org.ergoplatform.explorer.db.models.{Asset, UAsset}
import sttp.tapir.{Schema, Validator}

final case class AssetInfo(tokenId: TokenId, index: Int, amount: Long)
Expand All @@ -12,10 +12,13 @@ object AssetInfo {

def apply(asset: UAsset): AssetInfo = AssetInfo(asset.tokenId, asset.index, asset.amount)

def apply(asset: Asset): AssetInfo = AssetInfo(asset.tokenId, asset.index, asset.amount)

implicit val codec: Codec[AssetInfo] = deriveCodec

implicit val schema: Schema[AssetInfo] =
Schema.derive[AssetInfo]
Schema
.derive[AssetInfo]
.modify(_.tokenId)(_.description("Token ID"))
.modify(_.index)(_.description("Index of the asset in an output"))
.modify(_.amount)(_.description("Amount of tokens"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package org.ergoplatform.explorer.http.api.v0.models

import io.circe.Codec
import io.circe.generic.semiauto.deriveCodec
import org.ergoplatform.explorer.http.api.models.AssetInfo
import org.ergoplatform.explorer.{Address, TxId}
import sttp.tapir.{Schema, Validator}

final case class SearchResult(
blocks: List[BlockInfo],
transactions: List[TxId],
addresses: List[Address]
addresses: List[Address],
assets: List[AssetInfo]
)

object SearchResult {
Expand All @@ -21,6 +23,7 @@ object SearchResult {
.modify(_.blocks)(_.description("Blocks matching search query"))
.modify(_.transactions)(_.description("Ids of transactions matching search query"))
.modify(_.addresses)(_.description("Addresses matching search query"))
.modify(_.assets)(_.description("Assets matching search query"))

implicit val validator: Validator[SearchResult] = Validator.derive
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.ergoplatform.explorer.http.api.v0.modules

import cats.Monad
import org.ergoplatform.explorer.http.api.v0.models.SearchResult
import org.ergoplatform.explorer.http.api.v0.services.{
AddressesService,
AssetsService,
BlockChainService,
TransactionsService
}
import tofu.Start
import tofu.syntax.monadic._
import tofu.syntax.start._

trait Search[F[_]] {

def search(query: String): F[SearchResult]
}

object Search {

def apply[F[_]: Monad: Start](
blocks: BlockChainService[F],
transactions: TransactionsService[F],
addresses: AddressesService[F, fs2.Stream],
assets: AssetsService[F, fs2.Stream]
): Search[F] =
new Search[F] {

def search(query: String): F[SearchResult] =
for {
blocksF <- blocks.getBlocksByIdLike(query).start
txsF <- transactions.getIdsLike(query).start
addressesF <- addresses.getAllLike(query).start
assetsF <- assets.getAllLike(query).start
blocks <- blocksF.join
txs <- txsF.join
addresses <- addressesF.join
assets <- assetsF.join
} yield SearchResult(blocks, txs, addresses, assets)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import org.ergoplatform.explorer.CRaise
import org.ergoplatform.explorer.Err.{RefinementFailed, RequestProcessingErr}
import org.ergoplatform.explorer.db.Trans
import org.ergoplatform.explorer.db.algebra.LiftConnectionIO
import org.ergoplatform.explorer.http.api.v0.modules.Search
import org.ergoplatform.explorer.http.api.v0.services._
import org.ergoplatform.explorer.settings.{HttpSettings, ProtocolSettings, UtxCacheSettings}
import org.ergoplatform.explorer.settings.{ProtocolSettings, UtxCacheSettings}
import org.http4s.HttpRoutes
import sttp.tapir.server.http4s.Http4sServerOptions
import tofu.syntax.monadic._
Expand Down Expand Up @@ -45,6 +46,7 @@ object RoutesV0Bundle {
boxesService <- BoxesService(trans)
txsService <- TransactionsService(trans)
offchainService <- OffChainService(utxCacheSettings, redis)(trans)
search = Search(blockChainService, txsService, addressesService, assetsService)
blockRoutes = BlocksRoutes(blockChainService)
assetRoutes = AssetsRoutes(assetsService)
dexRoutes = DexRoutes(dexService)
Expand All @@ -54,9 +56,9 @@ object RoutesV0Bundle {
statsRoutes = StatsRoutes(statsService)
chartsRoutes = ChartsRoutes(statsService)
docsRoutes = DocsRoutes[F]
searchRoutes = SearchRoutes(blockChainService, txsService, addressesService)
searchRoutes = SearchRoutes(search)
boxesRoutes = BoxesRoutes(boxesService)
routes = infoRoutes <+> blockRoutes <+> assetRoutes <+> dexRoutes <+> txRoutes <+>
addressRoutes <+> statsRoutes <+> docsRoutes <+> searchRoutes <+> boxesRoutes <+> chartsRoutes
addressRoutes <+> statsRoutes <+> docsRoutes <+> searchRoutes <+> boxesRoutes <+> chartsRoutes
} yield RoutesV0Bundle(routes)
}
Original file line number Diff line number Diff line change
@@ -1,45 +1,32 @@
package org.ergoplatform.explorer.http.api.v0.routes

import cats.effect.{Concurrent, ContextShift, Sync, Timer}
import cats.syntax.flatMap._
import cats.syntax.functor._
import cats.effect.{Concurrent, ContextShift, Timer}
import io.chrisdavenport.log4cats.Logger
import org.ergoplatform.explorer.http.api.ApiErr
import org.ergoplatform.explorer.http.api.algebra.AdaptThrowable.AdaptThrowableEitherT
import org.ergoplatform.explorer.http.api.syntax.adaptThrowable._
import org.ergoplatform.explorer.http.api.v0.models.SearchResult
import org.ergoplatform.explorer.http.api.v0.services.{AddressesService, BlockChainService, TransactionsService}
import org.ergoplatform.explorer.http.api.v0.modules.Search
import org.http4s.HttpRoutes
import sttp.tapir.server.http4s._

final class SearchRoutes[
F[_]: Concurrent: ContextShift: Timer: AdaptThrowableEitherT[*[_], ApiErr]
](
blocksService: BlockChainService[F],
txsService: TransactionsService[F],
addressesService: AddressesService[F, fs2.Stream]
)(implicit opts: Http4sServerOptions[F]) {
](search: Search[F])(implicit opts: Http4sServerOptions[F]) {

import org.ergoplatform.explorer.http.api.v0.defs.SearchEndpointDefs._

val routes: HttpRoutes[F] = searchR

private def searchR: HttpRoutes[F] =
searchDef.toRoutes { q =>
(for {
blocks <- blocksService.getBlocksByIdLike(q)
txs <- txsService.getIdsLike(q)
addresses <- addressesService.getAllLike(q)
} yield SearchResult(blocks, txs, addresses)).adaptThrowable.value
search.search(q).adaptThrowable.value
}
}

object SearchRoutes {

def apply[F[_]: Concurrent: ContextShift: Timer: Logger](
blocksService: BlockChainService[F],
txsService: TransactionsService[F],
addressesService: AddressesService[F, fs2.Stream]
)(implicit opts: Http4sServerOptions[F]): HttpRoutes[F] =
new SearchRoutes(blocksService, txsService, addressesService).routes
def apply[F[_]: Concurrent: ContextShift: Timer: Logger](search: Search[F])(implicit
opts: Http4sServerOptions[F]
): HttpRoutes[F] =
new SearchRoutes(search).routes
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package org.ergoplatform.explorer.http.api.v0.services

import cats.{Functor, Monad, Traverse}
import cats.Monad
import cats.data.NonEmptyList
import cats.effect.Sync
import cats.syntax.list._
import cats.instances.list._
import cats.syntax.flatMap._
import cats.syntax.functor._
import cats.syntax.list._
import cats.syntax.traverse._
import cats.instances.list._
import fs2.Stream
import mouse.anyf._
import org.ergoplatform.explorer.Err.RequestProcessingErr.InconsistentDbData
import org.ergoplatform.explorer.db.Trans
import org.ergoplatform.explorer.db.algebra.LiftConnectionIO
import org.ergoplatform.explorer.db.repositories._
import org.ergoplatform.explorer.http.api.models.{Items, Paging}
import org.ergoplatform.explorer.http.api.models.{AssetInfo, Items, Paging}
import org.ergoplatform.explorer.http.api.v0.models.OutputInfo
import org.ergoplatform.explorer.syntax.stream._
import org.ergoplatform.explorer.{CRaise, TokenId}
Expand All @@ -33,6 +33,10 @@ trait AssetsService[F[_], S[_[_], _]] {
* according to EIP-4 https://github.com/ergoplatform/eips/blob/master/eip-0004.md
*/
def getIssuingBoxes(tokenIds: NonEmptyList[TokenId]): S[F, OutputInfo]

/** Get all assets matching a given `query`.
*/
def getAllLike(idSubstring: String): F[List[AssetInfo]]
}

object AssetsService {
Expand All @@ -50,29 +54,32 @@ object AssetsService {
extends AssetsService[F, Stream] {

def getAllIssuingBoxes(paging: Paging): F[Items[OutputInfo]] =
assetRepo.getIssuingBoxesQty.flatMap { total =>
assetRepo
.getAllIssuingBoxes(paging.offset, paging.limit)
.flatMap {
_.traverse(extOut =>
assetRepo.getAllByBoxId(extOut.output.boxId).map(OutputInfo(extOut, _))
)
}
.map(Items(_, total))
} ||> trans.xa
assetRepo.getIssuingBoxesQty
.flatMap { total =>
assetRepo
.getAllIssuingBoxes(paging.offset, paging.limit)
.flatMap {
_.traverse(extOut => assetRepo.getAllByBoxId(extOut.output.boxId).map(OutputInfo(extOut, _)))
}
.map(Items(_, total))
}
.thrushK(trans.xa)

def getIssuingBoxes(tokenIds: NonEmptyList[TokenId]): Stream[F, OutputInfo] =
(for {
extOuts <- assetRepo.getIssuingBoxesByTokenIds(tokenIds).asStream
boxIdsNel <- extOuts
.map(_.output.boxId)
.toNel
.orRaise[D](InconsistentDbData("Empty outputs"))
.asStream
.map(_.output.boxId)
.toNel
.orRaise[D](InconsistentDbData("Empty outputs"))
.asStream
assets <- assetRepo
.getAllByBoxIds(boxIdsNel)
.asStream
.getAllByBoxIds(boxIdsNel)
.asStream
outputInfo <- Stream.emits(OutputInfo.batch(extOuts, assets)).covary[D]
} yield outputInfo) ||> trans.xas
} yield outputInfo).thrushK(trans.xas)

def getAllLike(idSubstring: String): F[List[AssetInfo]] =
assetRepo.getAllLike(idSubstring).map(_.map(AssetInfo(_))).thrushK(trans.xa)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,10 @@ object AssetQuerySet extends QuerySet {
| left join node_outputs o on o.box_id = a.box_id where o.main_chain = true
|) as a
""".stripMargin.query[Int]

def getAllLike(idSubstring: String)(implicit lh: LogHandler): Query0[Asset] =
sql"""
|select a.token_id, a.box_id, a.token_id, a.index, a.value from node_assets a
|where a.token_id like ${"%" + idSubstring + "%"}
""".stripMargin.query[Asset]
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ trait AssetRepo[D[_], S[_[_], _]] {
/** Get total number of issuing boxes (on the main chain).
*/
def getIssuingBoxesQty: D[Int]

/** Get all assets matching a given `query`.
*/
def getAllLike(idSubstring: String): D[List[Asset]]
}

object AssetRepo {
Expand Down Expand Up @@ -104,5 +108,8 @@ object AssetRepo {

def getIssuingBoxesQty: D[Int] =
QS.getIssuingBoxesQty.unique.liftConnectionIO

def getAllLike(idSubstring: String): D[List[Asset]] =
QS.getAllLike(idSubstring).to[List].liftConnectionIO
}
}

0 comments on commit 2bb1dcb

Please sign in to comment.