diff --git a/ledger-service/db-backend/src/main/scala/com/digitalasset/http/dbbackend/Queries.scala b/ledger-service/db-backend/src/main/scala/com/digitalasset/http/dbbackend/Queries.scala index e2ac68acad9a..c919f005153e 100644 --- a/ledger-service/db-backend/src/main/scala/com/digitalasset/http/dbbackend/Queries.scala +++ b/ledger-service/db-backend/src/main/scala/com/digitalasset/http/dbbackend/Queries.scala @@ -203,13 +203,13 @@ sealed abstract class Queries(tablePrefix: String, tpIdCacheMaxEntries: Long)(im ) } - final def lastOffset(parties: OneAnd[Set, String], tpid: SurrogateTpId)(implicit + final def lastOffset(parties: PartySet, tpid: SurrogateTpId)(implicit log: LogHandler ): ConnectionIO[Map[String, String]] = { import Queries.CompatImplicits.catsReducibleFromFoldable1 val q = sql""" SELECT party, last_offset FROM $ledgerOffsetTableName WHERE tpid = $tpid AND - """ ++ Fragments.in(fr"party", parties) + """ ++ Fragments.in(fr"party", parties.toF) q.query[(String, String)] .to[Vector] .map(_.toMap) @@ -326,7 +326,7 @@ sealed abstract class Queries(tablePrefix: String, tpIdCacheMaxEntries: Long)(im } private[http] final def selectContracts( - parties: OneAnd[Set, String], + parties: PartySet, tpid: SurrogateTpId, predicate: Fragment, )(implicit @@ -339,7 +339,7 @@ sealed abstract class Queries(tablePrefix: String, tpIdCacheMaxEntries: Long)(im * which query or queries produced each contract. */ private[http] def selectContractsMultiTemplate[Mark]( - parties: OneAnd[Set, String], + parties: PartySet, queries: ISeq[(SurrogateTpId, Fragment)], trackMatchIndices: MatchedQueryMarker[Mark], )(implicit @@ -399,7 +399,7 @@ sealed abstract class Queries(tablePrefix: String, tpIdCacheMaxEntries: Long)(im } private[http] final def fetchById( - parties: OneAnd[Set, String], + parties: PartySet, tpid: SurrogateTpId, contractId: String, )(implicit @@ -408,7 +408,7 @@ sealed abstract class Queries(tablePrefix: String, tpIdCacheMaxEntries: Long)(im selectContracts(parties, tpid, sql"c.contract_id = $contractId").option private[http] final def fetchByKey( - parties: OneAnd[Set, String], + parties: PartySet, tpid: SurrogateTpId, key: Hash, )(implicit @@ -434,6 +434,8 @@ sealed abstract class Queries(tablePrefix: String, tpIdCacheMaxEntries: Long)(im } object Queries { + type PartySet = NonEmpty[Set[String]] + sealed trait SurrogateTpIdTag val SurrogateTpId = Tag.of[SurrogateTpIdTag] type SurrogateTpId = Long @@ SurrogateTpIdTag // matches tpid (BIGINT) above @@ -729,13 +731,13 @@ private final class PostgresQueries(tablePrefix: String, tpIdCacheMaxEntries: Lo } private[http] override def selectContractsMultiTemplate[Mark]( - parties: OneAnd[Set, String], + parties: PartySet, queries: ISeq[(SurrogateTpId, Fragment)], trackMatchIndices: MatchedQueryMarker[Mark], )(implicit log: LogHandler ): Query0[DBContract[Mark, JsValue, JsValue, Vector[String]]] = { - val partyVector = parties.toVector + val partyVector: Vector[String] = parties.toVector import ipol.{gas, pas} queryByCondition( queries, @@ -898,7 +900,7 @@ private final class OracleQueries( } private[http] override def selectContractsMultiTemplate[Mark]( - parties: OneAnd[Set, String], + parties: PartySet, queries: ISeq[(SurrogateTpId, Fragment)], trackMatchIndices: MatchedQueryMarker[Mark], )(implicit @@ -920,7 +922,7 @@ private final class OracleQueries( signatories, observers, agreement_text ${rownum getOrElse fr""} FROM $contractTableName c JOIN $contractStakeholdersViewName cst ON (c.contract_id = cst.contract_id) - WHERE (${Fragments.in(fr"cst.stakeholder", parties)}) + WHERE (${Fragments.in(fr"cst.stakeholder", parties.toF)}) AND ($queriesCondition)""" rownum.fold(dupQ)(_ => sql"SELECT $outerSelectList FROM ($dupQ) WHERE rownumber = 1") }, diff --git a/ledger-service/fetch-contracts/src/main/scala/fetchcontracts/AcsTxStreams.scala b/ledger-service/fetch-contracts/src/main/scala/fetchcontracts/AcsTxStreams.scala index 91ba88d761b4..73557347f77b 100644 --- a/ledger-service/fetch-contracts/src/main/scala/fetchcontracts/AcsTxStreams.scala +++ b/ledger-service/fetch-contracts/src/main/scala/fetchcontracts/AcsTxStreams.scala @@ -12,11 +12,7 @@ import util.{AbsoluteBookmark, BeginBookmark, ContractStreamStep, InsertDeleteSt import util.IdentifierConverters.apiIdentifier import com.daml.ledger.api.v1.transaction.Transaction import com.daml.ledger.api.{v1 => lav1} -import scalaz.OneAnd._ -import scalaz.std.set._ import scalaz.syntax.tag._ -import scalaz.syntax.foldable._ -import scalaz.OneAnd private[daml] object AcsTxStreams { import util.AkkaStreamsDoobie.{last, max, project2} @@ -132,7 +128,7 @@ private[daml] object AcsTxStreams { } private[daml] def transactionFilter( - parties: OneAnd[Set, domain.Party], + parties: domain.PartySet, templateIds: List[TemplateId.RequiredPkg], ): lav1.transaction_filter.TransactionFilter = { import lav1.transaction_filter._ @@ -141,6 +137,8 @@ private[daml] object AcsTxStreams { if (templateIds.isEmpty) Filters.defaultInstance else Filters(Some(lav1.transaction_filter.InclusiveFilters(templateIds.map(apiIdentifier)))) - TransactionFilter(domain.Party.unsubst(parties.toVector).map(_ -> filters).toMap) + TransactionFilter( + domain.Party.unsubst((parties: Set[domain.Party]).toVector).map(_ -> filters).toMap + ) } } diff --git a/ledger-service/fetch-contracts/src/main/scala/fetchcontracts/domain.scala b/ledger-service/fetch-contracts/src/main/scala/fetchcontracts/domain.scala index 4ad76b608b86..a386d528854a 100644 --- a/ledger-service/fetch-contracts/src/main/scala/fetchcontracts/domain.scala +++ b/ledger-service/fetch-contracts/src/main/scala/fetchcontracts/domain.scala @@ -8,6 +8,7 @@ import lf.data.Ref import util.ClientUtil.boxedRecord import com.daml.ledger.api.{v1 => lav1} import com.daml.ledger.api.refinements.{ApiTypes => lar} +import com.daml.scalautil.nonempty.NonEmpty import scalaz.std.list._ import scalaz.std.option._ import scalaz.std.string._ @@ -26,6 +27,8 @@ package object domain { type Party = lar.Party val Party = lar.Party + type PartySet = NonEmpty[Set[Party]] + type Offset = String @@ OffsetTag private[daml] implicit final class `fc domain ErrorOps`[A](private val o: Option[A]) @@ -161,6 +164,7 @@ package domain { final val ContractId = here.ContractId type Party = here.Party final val Party = here.Party + type PartySet = here.PartySet type Offset = here.Offset final val Offset = here.Offset type ActiveContract[+LfV] = here.ActiveContract[LfV] diff --git a/ledger-service/http-json/BUILD.bazel b/ledger-service/http-json/BUILD.bazel index a1246e7b6ab2..3fbf1169299f 100644 --- a/ledger-service/http-json/BUILD.bazel +++ b/ledger-service/http-json/BUILD.bazel @@ -531,6 +531,7 @@ da_scala_benchmark_jmh( "//libs-scala/doobie-slf4j", "//libs-scala/oracle-testing", "//libs-scala/ports", + "//libs-scala/scala-utils", "@maven//:com_oracle_database_jdbc_ojdbc8", "@maven//:io_dropwizard_metrics_metrics_core", "@maven//:org_slf4j_slf4j_api", diff --git a/ledger-service/http-json/src/bench/scala/com/daml/http/dbbackend/QueryBenchmark.scala b/ledger-service/http-json/src/bench/scala/com/daml/http/dbbackend/QueryBenchmark.scala index 5c5090289f4f..d4b5611ac2b3 100644 --- a/ledger-service/http-json/src/bench/scala/com/daml/http/dbbackend/QueryBenchmark.scala +++ b/ledger-service/http-json/src/bench/scala/com/daml/http/dbbackend/QueryBenchmark.scala @@ -6,9 +6,12 @@ package com.daml.http.dbbackend import com.daml.http.dbbackend.Queries.SurrogateTpId import com.daml.http.domain.{Party, TemplateId} import com.daml.http.util.Logging.instanceUUIDLogCtx +import com.daml.scalautil.Statement.discard +import com.daml.scalautil.nonempty.NonEmpty import doobie.implicits._ import org.openjdk.jmh.annotations._ -import scalaz.OneAnd + +import scala.collection.compat._ class QueryBenchmark extends ContractDaoBenchmark { @Param(Array("1", "5", "9")) @@ -46,9 +49,13 @@ class QueryBenchmark extends ContractDaoBenchmark { implicit val driver: SupportedJdbcDriver.TC = dao.jdbcDriver val result = instanceUUIDLogCtx(implicit lc => dao - .transact(ContractDao.selectContracts(OneAnd(Party(party), Set.empty), tpid, fr"1 = 1")) + .transact( + ContractDao.selectContracts(NonEmpty.pour(Party(party)) into Set, tpid, fr"1 = 1") + ) .unsafeRunSync() ) assert(result.size == batchSize) } + + discard(IterableOnce) // only needed for scala 2.12 } diff --git a/ledger-service/http-json/src/bench/scala/com/daml/http/dbbackend/QueryPayloadBenchmark.scala b/ledger-service/http-json/src/bench/scala/com/daml/http/dbbackend/QueryPayloadBenchmark.scala index b6ec0f2261c6..d8edbeed3440 100644 --- a/ledger-service/http-json/src/bench/scala/com/daml/http/dbbackend/QueryPayloadBenchmark.scala +++ b/ledger-service/http-json/src/bench/scala/com/daml/http/dbbackend/QueryPayloadBenchmark.scala @@ -10,10 +10,13 @@ import com.daml.http.dbbackend.Queries.SurrogateTpId import com.daml.http.domain.{Party, TemplateId} import com.daml.http.query.ValuePredicate import com.daml.http.util.Logging.instanceUUIDLogCtx +import com.daml.scalautil.Statement.discard +import com.daml.scalautil.nonempty.NonEmpty import org.openjdk.jmh.annotations._ -import scalaz.OneAnd import spray.json._ +import scala.collection.compat._ + class QueryPayloadBenchmark extends ContractDaoBenchmark { @Param(Array("1", "10", "100")) var extraParties: Int = _ @@ -70,9 +73,13 @@ class QueryPayloadBenchmark extends ContractDaoBenchmark { implicit val sjd: SupportedJdbcDriver.TC = dao.jdbcDriver val result = instanceUUIDLogCtx(implicit lc => dao - .transact(ContractDao.selectContracts(OneAnd(Party(party), Set.empty), tpid, whereClause)) + .transact( + ContractDao.selectContracts(NonEmpty.pour(Party(party)) into Set, tpid, whereClause) + ) .unsafeRunSync() ) assert(result.size == batchSize) } + + discard(IterableOnce) // only needed for scala 2.12 } diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/ContractsFetch.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/ContractsFetch.scala index 532004109a47..ed59a5139dd8 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/ContractsFetch.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/ContractsFetch.scala @@ -28,8 +28,6 @@ import com.daml.ledger.api.{v1 => lav1} import com.daml.logging.{ContextualizedLogger, LoggingContextOf} import doobie.free.{connection => fconn} import fconn.ConnectionIO -import scalaz.OneAnd._ -import scalaz.std.set._ import scalaz.std.vector._ import scalaz.std.list._ import scalaz.std.option.none @@ -39,7 +37,7 @@ import scalaz.syntax.functor._ import scalaz.syntax.foldable._ import scalaz.syntax.order._ import scalaz.syntax.std.option._ -import scalaz.{OneAnd, \/} +import scalaz.\/ import spray.json.{JsNull, JsValue} import scala.concurrent.ExecutionContext @@ -64,7 +62,7 @@ private class ContractsFetch( def fetchAndPersistBracket[A]( jwt: Jwt, ledgerId: LedgerApiDomain.LedgerId, - parties: OneAnd[Set, domain.Party], + parties: domain.PartySet, templateIds: List[domain.TemplateId.RequiredPkg], )(within: BeginBookmark[Terminates.AtAbsolute] => ConnectionIO[A])(implicit ec: ExecutionContext, @@ -87,7 +85,7 @@ private class ContractsFetch( // has desynchronized lagging <- (templateIds.toSet, bb.map(_.toDomain)) match { case (NonEmpty(tids), AbsoluteBookmark(expectedOff)) => - laggingOffsets(parties.toSet, expectedOff, tids) + laggingOffsets(parties, expectedOff, tids) case _ => fconn.pure(none[(domain.Offset, Set[domain.TemplateId.RequiredPkg])]) } retriedA <- lagging.cata( @@ -119,7 +117,7 @@ private class ContractsFetch( def fetchAndPersist( jwt: Jwt, ledgerId: LedgerApiDomain.LedgerId, - parties: OneAnd[Set, domain.Party], + parties: domain.PartySet, templateIds: List[domain.TemplateId.RequiredPkg], )(implicit ec: ExecutionContext, @@ -411,6 +409,6 @@ private[http] object ContractsFetch { private final case class FetchContext( jwt: Jwt, ledgerId: LedgerApiDomain.LedgerId, - parties: OneAnd[Set, domain.Party], + parties: domain.PartySet, ) } diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/ContractsService.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/ContractsService.scala index d80182de82c6..623da9710ebb 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/ContractsService.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/ContractsService.scala @@ -20,7 +20,6 @@ import com.daml.fetchcontracts.util.ContractStreamStep.{Acs, LiveBegin} import com.daml.http.util.FutureUtil.toFuture import com.daml.http.util.Logging.{InstanceUUID, RequestID} import com.daml.jwt.domain.Jwt -import com.daml.ledger.api.refinements.{ApiTypes => lar} import com.daml.ledger.api.v1.active_contracts_service.GetActiveContractsResponse import com.daml.ledger.api.{v1 => api} import com.daml.logging.{ContextualizedLogger, LoggingContextOf} @@ -69,7 +68,7 @@ class ContractsService( def resolveContractReference( jwt: Jwt, - parties: OneAnd[Set, domain.Party], + parties: domain.PartySet, contractLocator: domain.ContractLocator[LfValue], ledgerId: LedgerApiDomain.LedgerId, )(implicit @@ -113,7 +112,7 @@ class ContractsService( private[this] def findByContractKey( jwt: Jwt, - parties: OneAnd[Set, lar.Party], + parties: domain.PartySet, templateId: TemplateId.OptionalPkg, ledgerId: LedgerApiDomain.LedgerId, contractKey: LfValue, @@ -133,7 +132,7 @@ class ContractsService( private[this] def findByContractId( jwt: Jwt, - parties: OneAnd[Set, lar.Party], + parties: domain.PartySet, templateId: Option[domain.TemplateId.OptionalPkg], ledgerId: LedgerApiDomain.LedgerId, contractId: domain.ContractId, @@ -239,7 +238,7 @@ class ContractsService( def retrieveAll( jwt: Jwt, ledgerId: LedgerApiDomain.LedgerId, - parties: OneAnd[Set, domain.Party], + parties: domain.PartySet, )(implicit lc: LoggingContextOf[InstanceUUID] ): SearchResult[Error \/ domain.ActiveContract[LfValue]] = @@ -270,7 +269,7 @@ class ContractsService( def search( jwt: Jwt, ledgerId: LedgerApiDomain.LedgerId, - parties: OneAnd[Set, domain.Party], + parties: domain.PartySet, templateIds: OneAnd[Set, domain.TemplateId.OptionalPkg], queryParams: Map[String, JsValue], )(implicit @@ -384,7 +383,7 @@ class ContractsService( } private[this] def searchDbOneTpId_( - parties: OneAnd[Set, domain.Party], + parties: domain.PartySet, templateId: domain.TemplateId.RequiredPkg, queryParams: Map[String, JsValue], )(implicit @@ -399,7 +398,7 @@ class ContractsService( private[this] def searchInMemory( jwt: Jwt, ledgerId: LedgerApiDomain.LedgerId, - parties: OneAnd[Set, domain.Party], + parties: domain.PartySet, templateIds: Set[domain.TemplateId.RequiredPkg], queryParams: InMemoryQuery, )(implicit @@ -441,7 +440,7 @@ class ContractsService( private[this] def searchInMemoryOneTpId( jwt: Jwt, ledgerId: LedgerApiDomain.LedgerId, - parties: OneAnd[Set, domain.Party], + parties: domain.PartySet, templateId: domain.TemplateId.RequiredPkg, queryParams: InMemoryQuery.P, )(implicit @@ -469,7 +468,7 @@ class ContractsService( private[http] def liveAcsAsInsertDeleteStepSource( jwt: Jwt, ledgerId: LedgerApiDomain.LedgerId, - parties: OneAnd[Set, lar.Party], + parties: domain.PartySet, templateIds: List[domain.TemplateId.RequiredPkg], ): Source[ContractStreamStep.LAV1, NotUsed] = { val txnFilter = util.Transactions.transactionFilterFor(parties, templateIds) @@ -486,7 +485,7 @@ class ContractsService( private[http] def insertDeleteStepSource( jwt: Jwt, ledgerId: LedgerApiDomain.LedgerId, - parties: OneAnd[Set, lar.Party], + parties: domain.PartySet, templateIds: List[domain.TemplateId.RequiredPkg], startOffset: Option[domain.StartingOffset] = None, terminates: Terminates = Terminates.AtLedgerEnd, @@ -571,7 +570,7 @@ object ContractsService { final case class SearchContext[Tids[_], Pkgs[_]]( jwt: Jwt, - parties: OneAnd[Set, lar.Party], + parties: domain.PartySet, templateIds: Tids[domain.TemplateId[Pkgs[String]]], ledgerId: LedgerApiDomain.LedgerId, ) diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/PartiesService.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/PartiesService.scala index 2256991d4273..560c60f7251e 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/PartiesService.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/PartiesService.scala @@ -6,6 +6,7 @@ package com.daml.http import com.daml.lf.data.Ref import com.daml.http.EndpointsCompanion.{Error, InvalidUserInput, Unauthorized} import com.daml.http.util.FutureUtil._ +import com.daml.scalautil.nonempty._ import com.daml.jwt.domain.Jwt import com.daml.ledger.api import LedgerClientJwt.Grpc @@ -52,7 +53,7 @@ class PartiesService( def parties( jwt: Jwt, - identifiers: OneAnd[Set, domain.Party], + identifiers: domain.PartySet, ): Future[Error \/ (Set[domain.PartyDetails], Set[domain.Party])] = { val et: ET[(Set[domain.PartyDetails], Set[domain.Party])] = for { apiPartyIds <- either(toLedgerApiPartySet(identifiers)): ET[OneAnd[Set, Ref.Party]] @@ -68,16 +69,10 @@ class PartiesService( private def findUnknownParties( found: Set[domain.PartyDetails], - requested: OneAnd[Set, domain.Party], - ): Set[domain.Party] = { - import scalaz.std.iterable._ - import scalaz.syntax.foldable._ - - val requestedSet: Set[domain.Party] = requested.toSet - - if (found.size == requestedSet.size) Set.empty - else requestedSet -- found.map(_.identifier) - } + requested: domain.PartySet, + ): Set[domain.Party] = + if (found.size == requested.size) Set.empty + else requested -- found.map(_.identifier) } object PartiesService { @@ -89,12 +84,11 @@ object PartiesService { Unauthorized(e.message) def toLedgerApiPartySet( - ps: OneAnd[Set, domain.Party] + ps: domain.PartySet ): InvalidUserInput \/ OneAnd[Set, Ref.Party] = { import scalaz.std.list._ - val nel: OneAnd[List, domain.Party] = OneAnd(ps.head, ps.tail.toList) - val enel: InvalidUserInput \/ OneAnd[List, Ref.Party] = nel.traverse(toLedgerApi) - enel.map(xs => OneAnd(xs.head, xs.tail.toSet)) + val enel: InvalidUserInput \/ NonEmptyF[List, Ref.Party] = ps.toList.toF traverse toLedgerApi + enel.map { case x +-: xs => OneAnd(x, xs.toSet) } } def toLedgerApi(p: domain.Party): InvalidUserInput \/ Ref.Party = diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/WebSocketService.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/WebSocketService.scala index 84ea4540b4bd..77c3d27bc5ca 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/WebSocketService.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/WebSocketService.scala @@ -67,7 +67,7 @@ object WebSocketService { resolved: Set[domain.TemplateId.RequiredPkg], unresolved: Set[domain.TemplateId.OptionalPkg], fn: (domain.ActiveContract[LfV], Option[domain.Offset]) => Option[Positive], - dbQuery: (OneAnd[Set, domain.Party], dbbackend.ContractDao) => ConnectionIO[ + dbQuery: (domain.PartySet, dbbackend.ContractDao) => ConnectionIO[ _ <: Vector[(domain.ActiveContract[JsValue], Positive)] ], ) @@ -721,7 +721,7 @@ class WebSocketService( predicate: StreamPredicate[Positive], jwt: Jwt, ledgerId: LedgerApiDomain.LedgerId, - parties: OneAnd[Set, domain.Party], + parties: domain.PartySet, )(implicit lc: LoggingContextOf[InstanceUUID] ): Future[Source[StepAndErrors[Positive, JsValue], NotUsed]] = @@ -758,7 +758,7 @@ class WebSocketService( private def getTransactionSourceForParty[A: StreamQuery]( jwt: Jwt, ledgerId: LedgerApiDomain.LedgerId, - parties: OneAnd[Set, domain.Party], + parties: domain.PartySet, offPrefix: Option[domain.StartingOffset], rawRequest: A, )(implicit diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/dbbackend/ContractDao.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/dbbackend/ContractDao.scala index 9a7f5a386c8d..4e9e2fb807bb 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/dbbackend/ContractDao.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/dbbackend/ContractDao.scala @@ -22,7 +22,7 @@ import doobie.free.{connection => fconn} import doobie.implicits._ import doobie.util.log import org.slf4j.LoggerFactory -import scalaz.{Equal, NonEmptyList, OneAnd, Order, Semigroup} +import scalaz.{Equal, NonEmptyList, Order, Semigroup} import scalaz.std.set._ import scalaz.std.vector._ import scalaz.syntax.tag._ @@ -84,6 +84,11 @@ object ContractDao { def supportedJdbcDriverNames(available: Set[String]): Set[String] = supportedJdbcDrivers.keySet intersect available + private[this] def queriesPartySet(dps: domain.PartySet): Queries.PartySet = { + type NES[A] = NonEmpty[Set[A]] + domain.Party.unsubst[NES, String](dps) + } + def apply( cfg: JdbcConfig, tpIdCacheMaxEntries: Option[Long] = None, @@ -131,8 +136,7 @@ object ContractDao { def initialize(implicit log: LogHandler, sjd: SupportedJdbcDriver.TC): ConnectionIO[Unit] = sjd.q.queries.dropAllTablesIfExist *> sjd.q.queries.initDatabase - def lastOffset(parties: OneAnd[Set, domain.Party], templateId: domain.TemplateId.RequiredPkg)( - implicit + def lastOffset(parties: domain.PartySet, templateId: domain.TemplateId.RequiredPkg)(implicit log: LogHandler, sjd: SupportedJdbcDriver.TC, lc: LoggingContextOf[InstanceUUID], @@ -141,7 +145,7 @@ object ContractDao { for { tpId <- surrogateTemplateId(templateId) offset <- queries - .lastOffset(domain.Party.unsubst(parties), tpId) + .lastOffset(queriesPartySet(parties), tpId) } yield { type L[a] = Map[a, domain.Offset] domain.Party.subst[L, String](domain.Offset.tag.subst(offset)) @@ -269,7 +273,7 @@ object ContractDao { } def updateOffset( - parties: OneAnd[Set, domain.Party], + parties: domain.PartySet, templateId: domain.TemplateId.RequiredPkg, newOffset: domain.Offset, lastOffsets: Map[domain.Party, domain.Offset], @@ -280,10 +284,8 @@ object ContractDao { ): ConnectionIO[Unit] = { import cats.implicits._ import sjd.q.queries - import scalaz.OneAnd._ - import scalaz.std.set._ import scalaz.syntax.foldable._ - val partyVector = domain.Party.unsubst(parties.toVector) + val partyVector = domain.Party.unsubst(parties.toVector: Vector[domain.Party]) val lastOffsetsStr: Map[String, String] = domain.Party.unsubst[Map[*, String], String](domain.Offset.tag.unsubst(lastOffsets)) for { @@ -303,7 +305,7 @@ object ContractDao { @SuppressWarnings(Array("org.wartremover.warts.Any")) def selectContracts( - parties: OneAnd[Set, domain.Party], + parties: domain.PartySet, templateId: domain.TemplateId.RequiredPkg, predicate: doobie.Fragment, )(implicit @@ -316,14 +318,14 @@ object ContractDao { tpId <- surrogateTemplateId(templateId) dbContracts <- queries - .selectContracts(domain.Party.unsubst(parties), tpId, predicate) + .selectContracts(queriesPartySet(parties), tpId, predicate) .to[Vector] domainContracts = dbContracts.map(toDomain(templateId)) } yield domainContracts } private[http] def selectContractsMultiTemplate[Pos]( - parties: OneAnd[Set, domain.Party], + parties: domain.PartySet, predicates: Seq[(domain.TemplateId.RequiredPkg, doobie.Fragment)], trackMatchIndices: MatchedQueryMarker[Pos], )(implicit @@ -344,7 +346,7 @@ object ContractDao { for { dbContracts <- sjdQueries .selectContractsMultiTemplate( - domain.Party unsubst parties, + queriesPartySet(parties), queries, Queries.MatchedQueryMarker.ByInt, ) @@ -359,7 +361,7 @@ object ContractDao { for { dbContracts <- sjdQueries .selectContractsMultiTemplate( - domain.Party unsubst parties, + queriesPartySet(parties), queries, Queries.MatchedQueryMarker.Unused, ) @@ -377,7 +379,7 @@ object ContractDao { } private[http] def fetchById( - parties: OneAnd[Set, domain.Party], + parties: domain.PartySet, templateId: domain.TemplateId.RequiredPkg, contractId: domain.ContractId, )(implicit @@ -389,7 +391,7 @@ object ContractDao { for { tpId <- surrogateTemplateId(templateId) dbContracts <- queries.fetchById( - domain.Party unsubst parties, + queriesPartySet(parties), tpId, domain.ContractId unwrap contractId, ) @@ -397,7 +399,7 @@ object ContractDao { } private[http] def fetchByKey( - parties: OneAnd[Set, domain.Party], + parties: domain.PartySet, templateId: domain.TemplateId.RequiredPkg, key: Hash, )(implicit @@ -408,7 +410,7 @@ object ContractDao { import sjd.q._ for { tpId <- surrogateTemplateId(templateId) - dbContracts <- queries.fetchByKey(domain.Party unsubst parties, tpId, key) + dbContracts <- queries.fetchByKey(queriesPartySet(parties), tpId, key) } yield dbContracts.map(toDomain(templateId)) } @@ -442,7 +444,7 @@ object ContractDao { } final case class StaleOffsetException( - parties: OneAnd[Set, domain.Party], + parties: domain.PartySet, templateId: domain.TemplateId.RequiredPkg, newOffset: domain.Offset, lastOffset: Map[domain.Party, domain.Offset], diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala index 9830c2f4fa48..21f5d8dd7b81 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala @@ -7,6 +7,8 @@ import akka.http.scaladsl.model.{StatusCode, StatusCodes} import com.daml.lf.iface import com.daml.ledger.api.refinements.{ApiTypes => lar} import com.daml.ledger.api.{v1 => lav1} +import com.daml.scalautil.nonempty.NonEmpty +import com.daml.scalautil.nonempty.NonEmptyReturningOps._ import scalaz.Isomorphism.{<~>, IsoFunctorTemplate} import scalaz.std.list._ import scalaz.std.option._ @@ -22,9 +24,6 @@ object domain extends com.daml.fetchcontracts.domain.Aliases { import com.daml.fetchcontracts.domain.`fc domain ErrorOps` - private def oneAndSet[A](p: A, sp: Set[A]) = - OneAnd(p, sp - p) - trait JwtPayloadTag trait JwtPayloadG { @@ -32,7 +31,7 @@ object domain extends com.daml.fetchcontracts.domain.Aliases { val applicationId: ApplicationId val readAs: List[Party] val actAs: List[Party] - val parties: OneAnd[Set, Party] + val parties: PartySet } // Until we get multi-party submissions, write endpoints require a single party in actAs but we @@ -44,8 +43,8 @@ object domain extends com.daml.fetchcontracts.domain.Aliases { readAs: List[Party], ) extends JwtPayloadG { override val actAs: List[Party] = submitter.toList - override val parties: OneAnd[Set, Party] = - oneAndSet(actAs.head, actAs.tail.toSet union readAs.toSet) + override val parties: PartySet = + actAs.tail.toSet union readAs.toSet incl1 actAs.head } final case class JwtPayloadLedgerIdOnly(ledgerId: LedgerId) @@ -57,7 +56,7 @@ object domain extends com.daml.fetchcontracts.domain.Aliases { applicationId: ApplicationId, readAs: List[Party], actAs: List[Party], - parties: OneAnd[Set, Party], + parties: PartySet, ) extends JwtPayloadG {} object JwtPayload { @@ -68,11 +67,11 @@ object domain extends com.daml.fetchcontracts.domain.Aliases { actAs: List[Party], ): Option[JwtPayload] = { (readAs ++ actAs) match { - case Nil => None - case p :: ps => + case NonEmpty(ps) => Some( - new JwtPayload(ledgerId, applicationId, readAs, actAs, oneAndSet(p, ps.toSet)) {} + new JwtPayload(ledgerId, applicationId, readAs, actAs, ps.toSet) {} ) + case _ => None } } } diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/util/Collections.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/util/Collections.scala index 55debf8ac635..50eea4736d50 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/util/Collections.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/util/Collections.scala @@ -3,7 +3,9 @@ package com.daml.http.util -import scalaz.{NonEmptyList, OneAnd} +import com.daml.scalautil.nonempty.NonEmpty +import com.daml.scalautil.nonempty.NonEmptyReturningOps._ +import scalaz.NonEmptyList object Collections { @@ -12,8 +14,8 @@ object Collections { self.list.collect(f).toNel } - def toNonEmptySet[A](as: NonEmptyList[A]): OneAnd[Set, A] = { + def toNonEmptySet[A](as: NonEmptyList[A]): NonEmpty[Set[A]] = { import scalaz.syntax.foldable._ - OneAnd(as.head, as.tail.toSet - as.head) + as.tail.toSet incl1 as.head } } diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/util/Transactions.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/util/Transactions.scala index 029adae6ff71..372cb7584dfd 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/util/Transactions.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/util/Transactions.scala @@ -4,16 +4,12 @@ package com.daml.http.util import com.daml.lf.data.ImmArray.ImmArraySeq -import com.daml.http.domain.TemplateId +import com.daml.http.domain.{PartySet, TemplateId} import com.daml.fetchcontracts.util.IdentifierConverters.apiIdentifier import com.daml.ledger.api.v1.event.{ArchivedEvent, CreatedEvent} import com.daml.ledger.api.v1.transaction.Transaction import com.daml.ledger.api.v1.transaction_filter.{Filters, InclusiveFilters, TransactionFilter} import com.daml.ledger.api.refinements.{ApiTypes => lar} -import scalaz.OneAnd -import scalaz.OneAnd._ -import scalaz.std.set._ -import scalaz.syntax.foldable._ import scala.collection.compat._ @@ -27,12 +23,12 @@ object Transactions { transaction.events.iterator.flatMap(_.event.archived.toList).to(ImmArraySeq) def transactionFilterFor( - parties: OneAnd[Set, lar.Party], + parties: PartySet, templateIds: List[TemplateId.RequiredPkg], ): TransactionFilter = { val filters = if (templateIds.isEmpty) Filters.defaultInstance else Filters(Some(InclusiveFilters(templateIds.map(apiIdentifier)))) - TransactionFilter(lar.Party.unsubst(parties.toVector).map(_ -> filters).toMap) + TransactionFilter(lar.Party.unsubst((parties: Set[lar.Party]).toVector).map(_ -> filters).toMap) } } diff --git a/ledger-service/http-json/src/test/scala/com/digitalasset/http/DomainSpec.scala b/ledger-service/http-json/src/test/scala/com/digitalasset/http/DomainSpec.scala index 0b2b7e9b2e20..f3dbc016d832 100644 --- a/ledger-service/http-json/src/test/scala/com/digitalasset/http/DomainSpec.scala +++ b/ledger-service/http-json/src/test/scala/com/digitalasset/http/DomainSpec.scala @@ -4,9 +4,12 @@ package com.daml.http import domain._ +import com.daml.scalautil.nonempty.NonEmpty import org.scalatest.freespec.AnyFreeSpec import org.scalatest.matchers.should.Matchers -import scalaz.{NonEmptyList, OneAnd} +import scalaz.NonEmptyList + +import scala.collection.compat._ final class DomainSpec extends AnyFreeSpec with Matchers { private val ledgerId = LedgerId("myledger") @@ -17,13 +20,13 @@ final class DomainSpec extends AnyFreeSpec with Matchers { "parties deduplicates between actAs/submitter and readAs" in { val payload = JwtWritePayload(ledgerId, appId, submitter = NonEmptyList(alice), readAs = List(alice, bob)) - payload.parties shouldBe OneAnd(alice, Set(bob)) + payload.parties should ===(NonEmpty.pour(alice, bob) into Set) } } "JwtPayload" - { "parties deduplicates between actAs and readAs" in { val payload = JwtPayload(ledgerId, appId, actAs = List(alice), readAs = List(alice, bob)) - payload.map(_.parties) shouldBe Some(OneAnd(alice, Set(bob))) + payload.map(_.parties) should ===(Some(NonEmpty.pour(alice, bob) into Set)) } "returns None if readAs and actAs are empty" in { val payload = JwtPayload(ledgerId, appId, actAs = List(), readAs = List()) diff --git a/libs-scala/scala-utils/src/main/scala/com/daml/scalautil/nonempty/NonEmpty.scala b/libs-scala/scala-utils/src/main/scala/com/daml/scalautil/nonempty/NonEmpty.scala index 47787c29202f..fd67712be7e4 100644 --- a/libs-scala/scala-utils/src/main/scala/com/daml/scalautil/nonempty/NonEmpty.scala +++ b/libs-scala/scala-utils/src/main/scala/com/daml/scalautil/nonempty/NonEmpty.scala @@ -16,6 +16,7 @@ import NonEmptyCollCompat._ * these members. */ sealed abstract class NonEmptyColl { + import NonEmptyColl.Pouring /** Use its alias [[com.daml.scalautil.nonempty.NonEmpty]]. */ type NonEmpty[+A] @@ -41,7 +42,16 @@ sealed abstract class NonEmptyColl { def equiv[F[_], A]: NonEmpty[F[A]] === NonEmptyF[F, A] /** Check whether `self` is non-empty; if so, return it as the non-empty subtype. */ - def apply[Self](self: Self with imm.Iterable[_]): Option[NonEmpty[Self]] + @deprecated("misleading apply, use `case NonEmpty(xs)` instead", since = "1.18.0") + final def apply[Self](self: Self with imm.Iterable[_]): Option[NonEmpty[Self]] = unapply(self) + + /** {{{ + * NonEmpty.pour(1, 2, 3) into List : NonEmpty[List[Int]] // with (1, 2, 3) as elements + * }}} + * + * The weird argument order is to support Scala 2.12. + */ + final def pour[A](x: A, xs: A*): Pouring[A] = new Pouring(x, xs: _*) /** In pattern matching, think of [[NonEmpty]] as a sub-case-class of every * [[imm.Iterable]]; matching `case NonEmpty(ne)` ''adds'' the non-empty type @@ -53,7 +63,7 @@ sealed abstract class NonEmptyColl { * The type-checker will not permit you to apply this to a value that already * has the [[NonEmpty]] type, so don't worry about redundant checks here. */ - def unapply[Self](self: Self with imm.Iterable[_]): Option[NonEmpty[Self]] = apply(self) + def unapply[Self](self: Self with imm.Iterable[_]): Option[NonEmpty[Self]] } /** If you ever have to import [[NonEmptyColl]] or anything from it, your Scala @@ -68,11 +78,23 @@ object NonEmptyColl extends NonEmptyCollInstances { override def subtype[A] = Liskov.refl[A] override def equiv[F[_], A] = Leibniz.refl - override def apply[Self](self: Self with imm.Iterable[_]) = + override def unapply[Self](self: Self with imm.Iterable[_]) = if (self.nonEmpty) Some(self) else None private[nonempty] override def unsafeNarrow[Self <: imm.Iterable[Any]](self: Self) = self } + final class Pouring[A](hd: A, tl: A*) { + import NonEmpty.{unsafeNarrow => un} + // XXX SC this can be done more efficiently by not supporting 2.12 + @SuppressWarnings(Array("org.wartremover.warts.NonUnitStatements")) + def into[C <: imm.Iterable[A]](into: Factory[A, C]): NonEmpty[C] = un { + val bb = into.newBuilder + bb += hd + bb ++= tl + bb.result() + } + } + implicit final class ReshapeOps[F[_], A](private val nfa: NonEmpty[F[A]]) extends AnyVal { def toF: NonEmptyF[F, A] = NonEmpty.equiv[F, A](nfa) } diff --git a/libs-scala/scala-utils/src/main/scala/com/daml/scalautil/nonempty/NonEmptyReturningOps.scala b/libs-scala/scala-utils/src/main/scala/com/daml/scalautil/nonempty/NonEmptyReturningOps.scala index 1cd4595fd062..64d8fcdb6ae0 100644 --- a/libs-scala/scala-utils/src/main/scala/com/daml/scalautil/nonempty/NonEmptyReturningOps.scala +++ b/libs-scala/scala-utils/src/main/scala/com/daml/scalautil/nonempty/NonEmptyReturningOps.scala @@ -3,7 +3,7 @@ package com.daml.scalautil.nonempty -import scala.collection.{immutable => imm}, imm.Map +import scala.collection.{immutable => imm}, imm.Map, imm.Set import NonEmptyCollCompat._ /** Functions where ''the receiver'' is non-empty can be found implicitly with @@ -20,4 +20,9 @@ object NonEmptyReturningOps { // ideas for extension: +-: and :-+ operators } + + implicit final class `NE Set Ops`[A](private val self: Set[A]) extends AnyVal { + import NonEmpty.{unsafeNarrow => un} + def incl1(elem: A): NonEmpty[Set[A]] = un(self + elem) + } }