diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/presentproof/controller/PresentProofController.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/presentproof/controller/PresentProofController.scala index ff9445cff6..dcab472a4e 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/presentproof/controller/PresentProofController.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/presentproof/controller/PresentProofController.scala @@ -37,8 +37,6 @@ trait PresentProofController { object PresentProofController { def toHttpError(error: PresentationError): ErrorResponse = error match - case PresentationError.RepositoryError(cause) => - ErrorResponse.internalServerError(title = "RepositoryError", detail = Some(cause.toString)) case PresentationError.RecordIdNotFound(recordId) => ErrorResponse.notFound(detail = Some(s"Record Id not found: $recordId")) case PresentationError.ThreadIdNotFound(thid) => @@ -66,8 +64,6 @@ object PresentProofController { ErrorResponse.internalServerError(detail = Some("Issued credential not found")) case PresentationError.PresentationDecodingError(_) => ErrorResponse.internalServerError(detail = Some("Presentation decoding error")) - case PresentationError.PresentationNotFoundError(_) => - ErrorResponse.notFound(detail = Some("Presentation no found")) case PresentationError.HolderBindingError(msg) => ErrorResponse.internalServerError(detail = Some(s"Holder binding error: $msg")) case PresentationError.MissingCredential => diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/presentproof/controller/PresentProofControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/presentproof/controller/PresentProofControllerImpl.scala index 0019e4eef0..f9ec59216c 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/presentproof/controller/PresentProofControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/presentproof/controller/PresentProofControllerImpl.scala @@ -108,7 +108,7 @@ class PresentProofControllerImpl( val result = for { records <- thid match case None => presentationService.getPresentationRecords(ignoreWithZeroRetries = false) - case Some(thid) => presentationService.getPresentationRecordByThreadId(DidCommID(thid)).map(_.toSeq) + case Some(thid) => presentationService.findPresentationRecordByThreadId(DidCommID(thid)).map(_.toSeq) } yield PresentationStatusPage( records.map(PresentationStatus.fromDomain) ) @@ -121,7 +121,7 @@ class PresentProofControllerImpl( )(implicit rc: RequestContext): ZIO[WalletAccessContext, ErrorResponse, PresentationStatus] = { val result: ZIO[WalletAccessContext, ErrorResponse | PresentationError, PresentationStatus] = for { presentationId <- toDidCommID(id.toString) - maybeRecord <- presentationService.getPresentationRecord(presentationId) + maybeRecord <- presentationService.findPresentationRecord(presentationId) record <- ZIO .fromOption(maybeRecord) .mapError(_ => ErrorResponse.notFound(detail = Some(s"Presentation record not found: $id"))) diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/PresentationError.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/PresentationError.scala index afa97db727..775b447550 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/PresentationError.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/PresentationError.scala @@ -5,7 +5,6 @@ import org.hyperledger.identus.pollux.core.model.DidCommID sealed trait PresentationError object PresentationError { - final case class RepositoryError(cause: Throwable) extends PresentationError final case class RecordIdNotFound(recordId: DidCommID) extends PresentationError final case class ThreadIdNotFound(thid: DidCommID) extends PresentationError final case class InvalidFlowStateError(msg: String) extends PresentationError @@ -13,7 +12,6 @@ object PresentationError { final case class IssuedCredentialNotFoundError(cause: String) extends PresentationError final case class NotMatchingPresentationCredentialFormat(cause: Throwable) extends PresentationError final case class PresentationDecodingError(cause: String) extends PresentationError - final case class PresentationNotFoundError(cause: Throwable) extends PresentationError final case class HolderBindingError(msg: String) extends PresentationError object MissingCredential extends PresentationError object MissingCredentialFormat extends PresentationError diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/PresentationRepository.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/PresentationRepository.scala index 731b58c044..2e80f3af54 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/PresentationRepository.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/PresentationRepository.scala @@ -7,66 +7,72 @@ import org.hyperledger.identus.shared.models.WalletAccessContext import zio.* trait PresentationRepository { - def createPresentationRecord(record: PresentationRecord): RIO[WalletAccessContext, Int] - def getPresentationRecords(ignoreWithZeroRetries: Boolean): RIO[WalletAccessContext, Seq[PresentationRecord]] - def getPresentationRecord(recordId: DidCommID): RIO[WalletAccessContext, Option[PresentationRecord]] + def createPresentationRecord(record: PresentationRecord): URIO[WalletAccessContext, Unit] + + def getPresentationRecords(ignoreWithZeroRetries: Boolean): URIO[WalletAccessContext, Seq[PresentationRecord]] + + def findPresentationRecord(recordId: DidCommID): URIO[WalletAccessContext, Option[PresentationRecord]] + def getPresentationRecordsByStates( ignoreWithZeroRetries: Boolean, limit: Int, states: PresentationRecord.ProtocolState* - ): RIO[WalletAccessContext, Seq[PresentationRecord]] + ): URIO[WalletAccessContext, Seq[PresentationRecord]] def getPresentationRecordsByStatesForAllWallets( ignoreWithZeroRetries: Boolean, limit: Int, states: PresentationRecord.ProtocolState* - ): Task[Seq[PresentationRecord]] + ): UIO[Seq[PresentationRecord]] - def getPresentationRecordByThreadId(thid: DidCommID): RIO[WalletAccessContext, Option[PresentationRecord]] + def findPresentationRecordByThreadId(thid: DidCommID): URIO[WalletAccessContext, Option[PresentationRecord]] def updatePresentationRecordProtocolState( recordId: DidCommID, from: PresentationRecord.ProtocolState, to: PresentationRecord.ProtocolState - ): RIO[WalletAccessContext, Int] + ): URIO[WalletAccessContext, Unit] def updateWithRequestPresentation( recordId: DidCommID, request: RequestPresentation, protocolState: ProtocolState - ): RIO[WalletAccessContext, Int] + ): URIO[WalletAccessContext, Unit] + def updateWithProposePresentation( recordId: DidCommID, request: ProposePresentation, protocolState: ProtocolState - ): RIO[WalletAccessContext, Int] + ): URIO[WalletAccessContext, Unit] + def updateWithPresentation( recordId: DidCommID, presentation: Presentation, protocolState: ProtocolState - ): RIO[WalletAccessContext, Int] + ): URIO[WalletAccessContext, Unit] + def updatePresentationWithCredentialsToUse( recordId: DidCommID, credentialsToUse: Option[Seq[String]], protocolState: ProtocolState - ): RIO[WalletAccessContext, Int] + ): URIO[WalletAccessContext, Unit] def updateSDJWTPresentationWithCredentialsToUse( recordId: DidCommID, credentialsToUse: Option[Seq[String]], sdJwtClaimsToDisclose: Option[SdJwtCredentialToDisclose], protocolState: ProtocolState - ): RIO[WalletAccessContext, Int] + ): URIO[WalletAccessContext, Unit] def updateAnoncredPresentationWithCredentialsToUse( recordId: DidCommID, anoncredCredentialsToUseJsonSchemaId: Option[String], anoncredCredentialsToUse: Option[AnoncredCredentialProofs], protocolState: ProtocolState - ): RIO[WalletAccessContext, Int] + ): URIO[WalletAccessContext, Unit] def updateAfterFail( recordId: DidCommID, failReason: Option[String] - ): RIO[WalletAccessContext, Int] + ): URIO[WalletAccessContext, Unit] } diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/PresentationRepositoryInMemory.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/PresentationRepositoryInMemory.scala index c746c22965..6f784bad9c 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/PresentationRepositoryInMemory.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/PresentationRepositoryInMemory.scala @@ -1,11 +1,29 @@ package org.hyperledger.identus.pollux.core.repository +import cats.data.NonEmptyList +import doobie.* +import doobie.free.connection +import doobie.implicits.* +import doobie.postgres.* +import doobie.postgres.circe.json.implicits.* +import doobie.postgres.implicits.* +import io.circe +import io.circe.* +import io.circe.parser.* +import io.circe.syntax.* import org.hyperledger.identus.mercury.protocol.presentproof.* import org.hyperledger.identus.pollux.core.model.* import org.hyperledger.identus.pollux.core.model.error.PresentationError.* import org.hyperledger.identus.pollux.core.model.PresentationRecord.ProtocolState +import org.hyperledger.identus.pollux.core.repository.PresentationRepository +import org.hyperledger.identus.shared.db.ContextAwareTask +import org.hyperledger.identus.shared.db.Implicits.* import org.hyperledger.identus.shared.models.{WalletAccessContext, WalletId} import zio.* +import zio.interop.catz.* +import zio.json.* +import zio.json.ast.Json +import zio.json.ast.Json.* import java.time.Instant @@ -28,18 +46,20 @@ class PresentationRepositoryInMemory( }(ZIO.succeed) } yield walletRef - override def createPresentationRecord(record: PresentationRecord): RIO[WalletAccessContext, Int] = { - for { - storeRef <- walletStoreRef - _ <- for { - store <- storeRef.get - maybeRecord = store.values.find(_.thid == record.thid) - } yield () - _ <- storeRef.update(r => r + (record.id -> record)) - } yield 1 + override def createPresentationRecord(record: PresentationRecord): URIO[WalletAccessContext, Unit] = { + val result = + for { + storeRef <- walletStoreRef + _ <- for { + store <- storeRef.get + maybeRecord = store.values.find(_.thid == record.thid) + } yield () + _ <- storeRef.update(r => r + (record.id -> record)) + } yield 1 + result.ensureOneAffectedRowOrDie } - override def getPresentationRecord(recordId: DidCommID): RIO[WalletAccessContext, Option[PresentationRecord]] = { + override def findPresentationRecord(recordId: DidCommID): URIO[WalletAccessContext, Option[PresentationRecord]] = { for { storeRef <- walletStoreRef store <- storeRef.get @@ -49,7 +69,7 @@ class PresentationRepositoryInMemory( override def getPresentationRecords( ignoreWithZeroRetries: Boolean, - ): RIO[WalletAccessContext, Seq[PresentationRecord]] = { + ): URIO[WalletAccessContext, Seq[PresentationRecord]] = { for { storeRef <- walletStoreRef store <- storeRef.get @@ -60,153 +80,166 @@ class PresentationRepositoryInMemory( recordId: DidCommID, from: ProtocolState, to: ProtocolState - ): RIO[WalletAccessContext, Int] = { - for { - storeRef <- walletStoreRef - store <- storeRef.get - maybeRecord = store.find((id, record) => id == recordId && record.protocolState == from).map(_._2) - count <- maybeRecord - .map(record => - for { - _ <- storeRef.update(r => - r.updated( - recordId, - record.copy( - protocolState = to, - metaRetries = maxRetries, - metaLastFailure = None, + ): URIO[WalletAccessContext, Unit] = { + val result = + for { + storeRef <- walletStoreRef + store <- storeRef.get + maybeRecord = store.find((id, record) => id == recordId && record.protocolState == from).map(_._2) + count <- maybeRecord + .map(record => + for { + _ <- storeRef.update(r => + r.updated( + recordId, + record.copy( + protocolState = to, + metaRetries = maxRetries, + metaLastFailure = None, + ) ) ) - ) - } yield 1 - ) - .getOrElse(ZIO.succeed(0)) - } yield count + } yield 1 + ) + .getOrElse(ZIO.succeed(0)) + } yield count + result.ensureOneAffectedRowOrDie } override def updatePresentationWithCredentialsToUse( recordId: DidCommID, credentialsToUse: Option[Seq[String]], protocolState: ProtocolState - ): RIO[WalletAccessContext, Int] = { - for { - storeRef <- walletStoreRef - maybeRecord <- getPresentationRecord(recordId) - count <- maybeRecord - .map(record => - for { - _ <- storeRef.update(r => - r.updated( - recordId, - record.copy( - updatedAt = Some(Instant.now), - credentialsToUse = credentialsToUse.map(_.toList), - protocolState = protocolState, - metaRetries = maxRetries, - metaLastFailure = None, + ): URIO[WalletAccessContext, Unit] = { + val result = + for { + storeRef <- walletStoreRef + maybeRecord <- findPresentationRecord(recordId) + count <- maybeRecord + .map(record => + for { + _ <- storeRef.update(r => + r.updated( + recordId, + record.copy( + updatedAt = Some(Instant.now), + credentialsToUse = credentialsToUse.map(_.toList), + protocolState = protocolState, + metaRetries = maxRetries, + metaLastFailure = None, + ) ) ) - ) - } yield 1 - ) - .getOrElse(ZIO.succeed(0)) - } yield count + } yield 1 + ) + .getOrElse(ZIO.succeed(0)) + } yield count + result.ensureOneAffectedRowOrDie } + override def updateSDJWTPresentationWithCredentialsToUse( recordId: DidCommID, credentialsToUse: Option[Seq[String]], sdJwtClaimsToDisclose: Option[SdJwtCredentialToDisclose], protocolState: ProtocolState - ): RIO[WalletAccessContext, Int] = { - for { - storeRef <- walletStoreRef - maybeRecord <- getPresentationRecord(recordId) - count <- maybeRecord - .map(record => - for { - _ <- storeRef.update(r => - r.updated( - recordId, - record.copy( - updatedAt = Some(Instant.now), - credentialsToUse = credentialsToUse.map(_.toList), - sdJwtClaimsToDisclose = sdJwtClaimsToDisclose, - protocolState = protocolState, - metaRetries = maxRetries, - metaLastFailure = None, + ): URIO[WalletAccessContext, Unit] = { + val result: URIO[WalletAccessContext, Unit] = { + for { + storeRef <- walletStoreRef + maybeRecord <- findPresentationRecord(recordId) + result <- maybeRecord + .map(record => + for { + result <- storeRef.update(r => + r.updated( + recordId, + record.copy( + updatedAt = Some(Instant.now), + credentialsToUse = credentialsToUse.map(_.toList), + sdJwtClaimsToDisclose = sdJwtClaimsToDisclose, + protocolState = protocolState, + metaRetries = maxRetries, + metaLastFailure = None, + ) ) ) - ) - } yield 1 - ) - .getOrElse(ZIO.succeed(0)) - } yield count + } yield result + ) + .getOrElse(ZIO.succeed(0)) + } yield result + } + result.ensureOneAffectedRowOrDie } + def updateAnoncredPresentationWithCredentialsToUse( recordId: DidCommID, anoncredCredentialsToUseJsonSchemaId: Option[String], anoncredCredentialsToUse: Option[AnoncredCredentialProofs], protocolState: ProtocolState - ): RIO[WalletAccessContext, Int] = { - for { - storeRef <- walletStoreRef - maybeRecord <- getPresentationRecord(recordId) - count <- maybeRecord - .map(record => - for { - _ <- storeRef.update(r => - r.updated( - recordId, - record.copy( - updatedAt = Some(Instant.now), - anoncredCredentialsToUseJsonSchemaId = anoncredCredentialsToUseJsonSchemaId, - anoncredCredentialsToUse = anoncredCredentialsToUse, - protocolState = protocolState, - metaRetries = maxRetries, - metaLastFailure = None, + ): URIO[WalletAccessContext, Unit] = { + val result = + for { + storeRef <- walletStoreRef + maybeRecord <- findPresentationRecord(recordId) + count <- maybeRecord + .map(record => + for { + _ <- storeRef.update(r => + r.updated( + recordId, + record.copy( + updatedAt = Some(Instant.now), + anoncredCredentialsToUseJsonSchemaId = anoncredCredentialsToUseJsonSchemaId, + anoncredCredentialsToUse = anoncredCredentialsToUse, + protocolState = protocolState, + metaRetries = maxRetries, + metaLastFailure = None, + ) ) ) - ) - } yield 1 - ) - .getOrElse(ZIO.succeed(0)) - } yield count + } yield 1 + ) + .getOrElse(ZIO.succeed(0)) + } yield count + result.ensureOneAffectedRowOrDie } override def updateWithPresentation( recordId: DidCommID, presentation: Presentation, protocolState: ProtocolState - ): RIO[WalletAccessContext, Int] = { - for { - storeRef <- walletStoreRef - maybeRecord <- getPresentationRecord(recordId) - count <- maybeRecord - .map(record => - for { - _ <- storeRef.update(r => - r.updated( - recordId, - record.copy( - updatedAt = Some(Instant.now), - presentationData = Some(presentation), - protocolState = protocolState, - metaRetries = maxRetries, - metaLastFailure = None, + ): URIO[WalletAccessContext, Unit] = { + val result = + for { + storeRef <- walletStoreRef + maybeRecord <- findPresentationRecord(recordId) + count <- maybeRecord + .map(record => + for { + _ <- storeRef.update(r => + r.updated( + recordId, + record.copy( + updatedAt = Some(Instant.now), + presentationData = Some(presentation), + protocolState = protocolState, + metaRetries = maxRetries, + metaLastFailure = None, + ) ) ) - ) - } yield 1 - ) - .getOrElse(ZIO.succeed(0)) - } yield count + } yield 1 + ) + .getOrElse(ZIO.succeed(0)) + } yield count + result.ensureOneAffectedRowOrDie } override def getPresentationRecordsByStates( ignoreWithZeroRetries: Boolean, limit: Int, states: ProtocolState* - ): RIO[WalletAccessContext, Seq[PresentationRecord]] = { + ): URIO[WalletAccessContext, Seq[PresentationRecord]] = { for { storeRef <- walletStoreRef store <- storeRef.get @@ -220,7 +253,7 @@ class PresentationRepositoryInMemory( ignoreWithZeroRetries: Boolean, limit: Int, states: ProtocolState* - ): Task[Seq[PresentationRecord]] = { + ): UIO[Seq[PresentationRecord]] = { for { refs <- walletRefs.get stores <- ZIO.foreach(refs.values.toList)(_.get) @@ -236,9 +269,9 @@ class PresentationRepositoryInMemory( } } - override def getPresentationRecordByThreadId( + override def findPresentationRecordByThreadId( thid: DidCommID, - ): RIO[WalletAccessContext, Option[PresentationRecord]] = { + ): URIO[WalletAccessContext, Option[PresentationRecord]] = { for { storeRef <- walletStoreRef store <- storeRef.get @@ -249,85 +282,93 @@ class PresentationRepositoryInMemory( recordId: DidCommID, request: RequestPresentation, protocolState: ProtocolState - ): RIO[WalletAccessContext, Int] = { - for { - storeRef <- walletStoreRef - maybeRecord <- getPresentationRecord(recordId) - count <- maybeRecord - .map(record => - for { - _ <- storeRef.update(r => - r.updated( - recordId, - record.copy( - updatedAt = Some(Instant.now), - requestPresentationData = Some(request), - protocolState = protocolState, - metaRetries = maxRetries, - metaLastFailure = None, + ): URIO[WalletAccessContext, Unit] = { + val result = + for { + storeRef <- walletStoreRef + maybeRecord <- findPresentationRecord(recordId) + count <- maybeRecord + .map(record => + for { + _ <- storeRef.update(r => + r.updated( + recordId, + record.copy( + updatedAt = Some(Instant.now), + requestPresentationData = Some(request), + protocolState = protocolState, + metaRetries = maxRetries, + metaLastFailure = None, + ) ) ) - ) - } yield 1 - ) - .getOrElse(ZIO.succeed(0)) - } yield count + } yield 1 + ) + .getOrElse(ZIO.succeed(0)) + } yield count + result.ensureOneAffectedRowOrDie } + override def updateWithProposePresentation( recordId: DidCommID, request: ProposePresentation, protocolState: ProtocolState - ): RIO[WalletAccessContext, Int] = { - for { - storeRef <- walletStoreRef - maybeRecord <- getPresentationRecord(recordId) - count <- maybeRecord - .map(record => - for { - _ <- storeRef.update(r => - r.updated( - recordId, - record.copy( - updatedAt = Some(Instant.now), - proposePresentationData = Some(request), - protocolState = protocolState, - metaRetries = maxRetries, - metaLastFailure = None, + ): URIO[WalletAccessContext, Unit] = { + val result = + for { + storeRef <- walletStoreRef + maybeRecord <- findPresentationRecord(recordId) + count <- maybeRecord + .map(record => + for { + _ <- storeRef.update(r => + r.updated( + recordId, + record.copy( + updatedAt = Some(Instant.now), + proposePresentationData = Some(request), + protocolState = protocolState, + metaRetries = maxRetries, + metaLastFailure = None, + ) ) ) - ) - } yield 1 - ) - .getOrElse(ZIO.succeed(0)) - } yield count + } yield 1 + ) + .getOrElse(ZIO.succeed(0)) + } yield count + result.ensureOneAffectedRowOrDie } def updateAfterFail( recordId: DidCommID, failReason: Option[String] - ): RIO[WalletAccessContext, Int] = for { - storeRef <- walletStoreRef - maybeRecord <- getPresentationRecord(recordId) - count <- maybeRecord - .map(record => - for { - _ <- storeRef.update(r => - r.updated( - recordId, - record.copy( - metaRetries = math.max(0, record.metaRetries - 1), - metaNextRetry = - if (record.metaRetries - 1 <= 0) None - else Some(Instant.now().plusSeconds(60)), // TODO exponention time - metaLastFailure = failReason + ): URIO[WalletAccessContext, Unit] = { + val result = + for { + storeRef <- walletStoreRef + maybeRecord <- findPresentationRecord(recordId) + count <- maybeRecord + .map(record => + for { + _ <- storeRef.update(r => + r.updated( + recordId, + record.copy( + metaRetries = math.max(0, record.metaRetries - 1), + metaNextRetry = + if (record.metaRetries - 1 <= 0) None + else Some(Instant.now().plusSeconds(60)), // TODO exponention time + metaLastFailure = failReason + ) + ) ) - ) + } yield 1 ) - } yield 1 - ) - .getOrElse(ZIO.succeed(0)) - } yield count - + .getOrElse(ZIO.succeed(0)) + } yield count + result.ensureOneAffectedRowOrDie + } } object PresentationRepositoryInMemory { diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/MockPresentationService.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/MockPresentationService.scala index a68beec6cf..db99586e7b 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/MockPresentationService.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/MockPresentationService.scala @@ -244,9 +244,11 @@ object MockPresentationService extends Mock[PresentationService] { state: PresentationRecord.ProtocolState* ): IO[PresentationError, Seq[PresentationRecord]] = ??? - override def getPresentationRecord(recordId: DidCommID): IO[PresentationError, Option[PresentationRecord]] = ??? + override def findPresentationRecord(recordId: DidCommID): IO[PresentationError, Option[PresentationRecord]] = ??? - override def getPresentationRecordByThreadId(thid: DidCommID): IO[PresentationError, Option[PresentationRecord]] = + override def findPresentationRecordByThreadId( + thid: DidCommID + ): IO[PresentationError, Option[PresentationRecord]] = ??? override def receiveProposePresentation(request: ProposePresentation): IO[PresentationError, PresentationRecord] = diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/PresentationService.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/PresentationService.scala index 47475dcda4..ec2d34d16a 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/PresentationService.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/PresentationService.scala @@ -93,11 +93,11 @@ trait PresentationService { state: PresentationRecord.ProtocolState* ): IO[PresentationError, Seq[PresentationRecord]] - def getPresentationRecord( + def findPresentationRecord( recordId: DidCommID ): ZIO[WalletAccessContext, PresentationError, Option[PresentationRecord]] - def getPresentationRecordByThreadId( + def findPresentationRecordByThreadId( thid: DidCommID ): ZIO[WalletAccessContext, PresentationError, Option[PresentationRecord]] diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/PresentationServiceImpl.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/PresentationServiceImpl.scala index 4736208edc..a70e78f516 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/PresentationServiceImpl.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/PresentationServiceImpl.scala @@ -47,24 +47,16 @@ private class PresentationServiceImpl( ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] = { for { record <- getRecordWithState(recordId, ProtocolState.PresentationPending) - count <- presentationRepository - .updateWithPresentation(recordId, presentation, ProtocolState.PresentationGenerated) - .mapError(RepositoryError.apply) @@ CustomMetricsAspect.endRecordingTime( - s"${record.id}_present_proof_flow_prover_presentation_pending_to_generated_ms_gauge", - "present_proof_flow_prover_presentation_pending_to_generated_ms_gauge" - ) @@ CustomMetricsAspect.startRecordingTime( - s"${record.id}_present_proof_flow_prover_presentation_generated_to_sent_ms_gauge" - ) - _ <- count match - case 1 => ZIO.succeed(()) - case _ => ZIO.fail(RecordIdNotFound(recordId)) - record <- presentationRepository - .getPresentationRecord(recordId) - .mapError(RepositoryError.apply) - .flatMap { - case None => ZIO.fail(RecordIdNotFound(record.id)) - case Some(value) => ZIO.succeed(value) - } + count <- + presentationRepository + .updateWithPresentation(recordId, presentation, ProtocolState.PresentationGenerated) + @@ CustomMetricsAspect.endRecordingTime( + s"${record.id}_present_proof_flow_prover_presentation_pending_to_generated_ms_gauge", + "present_proof_flow_prover_presentation_pending_to_generated_ms_gauge" + ) @@ CustomMetricsAspect.startRecordingTime( + s"${record.id}_present_proof_flow_prover_presentation_generated_to_sent_ms_gauge" + ) + record <- getRecord(recordId) } yield record } @@ -75,12 +67,7 @@ private class PresentationServiceImpl( ): ZIO[WalletAccessContext, PresentationError, PresentationPayload] = { for { - maybeRecord <- presentationRepository - .getPresentationRecord(recordId) - .mapError(RepositoryError.apply) - record <- ZIO - .fromOption(maybeRecord) - .mapError(_ => RecordIdNotFound(recordId)) + record <- getRecord(recordId) credentialsToUse <- ZIO .fromOption(record.credentialsToUse) .mapError(_ => InvalidFlowStateError(s"No request found for this record: $recordId")) @@ -99,7 +86,6 @@ private class PresentationServiceImpl( ) ) ) - presentationPayload <- createJwtPresentationPayloadFromCredential( issuedCredentials, requestPresentation, @@ -114,12 +100,7 @@ private class PresentationServiceImpl( ): ZIO[WalletAccessContext, PresentationError, PresentationCompact] = { for { - maybeRecord <- presentationRepository - .getPresentationRecord(recordId) - .mapError(RepositoryError.apply) - record <- ZIO - .fromOption(maybeRecord) - .mapError(_ => RecordIdNotFound(recordId)) + record <- getRecord(recordId) credentialsToUse <- ZIO .fromOption(record.credentialsToUse) .mapError(_ => InvalidFlowStateError(s"No request found for this record: $recordId")) @@ -146,8 +127,7 @@ private class PresentationServiceImpl( presentationCompact <- createPresentationFromRecord( issuedCredentials, sdJwtClaimsToDisclose, - requestPresentation, - prover + requestPresentation ) presentationPayload <- ZIO.succeed(presentationCompact) @@ -190,12 +170,7 @@ private class PresentationServiceImpl( ): ZIO[WalletAccessContext, PresentationError, AnoncredPresentation] = { for { - maybeRecord <- presentationRepository - .getPresentationRecord(recordId) - .mapError(RepositoryError.apply) - record <- ZIO - .fromOption(maybeRecord) - .mapError(_ => RecordIdNotFound(recordId)) + record <- getRecord(recordId) requestPresentation <- ZIO .fromOption(record.requestPresentationData) .mapError(_ => InvalidFlowStateError(s"RequestPresentation not found: $recordId")) @@ -263,32 +238,18 @@ private class PresentationServiceImpl( override def getPresentationRecords( ignoreWithZeroRetries: Boolean - ): ZIO[WalletAccessContext, PresentationError, Seq[PresentationRecord]] = { - for { - records <- presentationRepository - .getPresentationRecords(ignoreWithZeroRetries) - .mapError(RepositoryError.apply) - } yield records - } + ): ZIO[WalletAccessContext, PresentationError, Seq[PresentationRecord]] = presentationRepository + .getPresentationRecords(ignoreWithZeroRetries) - override def getPresentationRecord( + override def findPresentationRecord( recordId: DidCommID - ): ZIO[WalletAccessContext, PresentationError, Option[PresentationRecord]] = { - for { - record <- presentationRepository - .getPresentationRecord(recordId) - .mapError(RepositoryError.apply) - } yield record - } + ): ZIO[WalletAccessContext, PresentationError, Option[PresentationRecord]] = + presentationRepository.findPresentationRecord(recordId) - override def getPresentationRecordByThreadId( + override def findPresentationRecordByThreadId( thid: DidCommID ): ZIO[WalletAccessContext, PresentationError, Option[PresentationRecord]] = - for { - record <- presentationRepository - .getPresentationRecordByThreadId(thid) - .mapError(RepositoryError.apply) - } yield record + presentationRepository.findPresentationRecordByThreadId(thid) override def rejectRequestPresentation( recordId: DidCommID @@ -403,13 +364,9 @@ private class PresentationServiceImpl( ) _ <- presentationRepository .createPresentationRecord(record) - .flatMap { - case 1 => ZIO.succeed(()) - case n => ZIO.fail(UnexpectedException(s"Invalid row count result: $n")) - } - .mapError(RepositoryError.apply) @@ CustomMetricsAspect.startRecordingTime( - s"${record.id}_present_proof_flow_verifier_req_pending_to_sent_ms_gauge" - ) + @@ CustomMetricsAspect.startRecordingTime( + s"${record.id}_present_proof_flow_verifier_req_pending_to_sent_ms_gauge" + ) } yield record } @@ -417,25 +374,17 @@ private class PresentationServiceImpl( ignoreWithZeroRetries: Boolean, limit: Int, states: PresentationRecord.ProtocolState* - ): ZIO[WalletAccessContext, PresentationError, Seq[PresentationRecord]] = { - for { - records <- presentationRepository - .getPresentationRecordsByStates(ignoreWithZeroRetries, limit, states*) - .mapError(RepositoryError.apply) - } yield records - } + ): ZIO[WalletAccessContext, PresentationError, Seq[PresentationRecord]] = + presentationRepository + .getPresentationRecordsByStates(ignoreWithZeroRetries, limit, states*) override def getPresentationRecordsByStatesForAllWallets( ignoreWithZeroRetries: Boolean, limit: Int, states: PresentationRecord.ProtocolState* - ): IO[PresentationError, Seq[PresentationRecord]] = { - for { - records <- presentationRepository - .getPresentationRecordsByStatesForAllWallets(ignoreWithZeroRetries, limit, states*) - .mapError(RepositoryError.apply) - } yield records - } + ): IO[PresentationError, Seq[PresentationRecord]] = + presentationRepository + .getPresentationRecordsByStatesForAllWallets(ignoreWithZeroRetries, limit, states*) override def receiveRequestPresentation( connectionId: Option[String], @@ -490,13 +439,7 @@ private class PresentationServiceImpl( metaLastFailure = None, ) ) - _ <- presentationRepository - .createPresentationRecord(record) - .flatMap { - case 1 => ZIO.succeed(()) - case n => ZIO.fail(UnexpectedException(s"Invalid row count result: $n")) - } - .mapError(RepositoryError.apply) + _ <- presentationRepository.createPresentationRecord(record) } yield record } @@ -504,16 +447,12 @@ private class PresentationServiceImpl( issuedCredentials: Seq[String], claimsToDisclose: SdJwtCredentialToDisclose, requestPresentation: RequestPresentation, - prover: Issuer ): IO[PresentationError, PresentationCompact] = { val verifiableCredentials: Either[ PresentationError.PresentationDecodingError, Seq[CredentialCompact] ] = issuedCredentials.map { signedCredential => - println("******signedCredential***********") - println(signedCredential) - println("*******signedCredential**********") decode[org.hyperledger.identus.mercury.model.Base64](signedCredential) .flatMap(x => Right(CredentialCompact.unsafeFromCompact(new String(java.util.Base64.getUrlDecoder.decode(x.base64)))) @@ -523,11 +462,9 @@ private class PresentationServiceImpl( }.sequence import io.circe.parser.decode - import io.circe.syntax._ + import io.circe.syntax.* + import java.util.Base64 - println("*****************") - println(requestPresentation.attachments.head) - println("*****************") val result: Either[PresentationDecodingError, SDJwtPresentation] = requestPresentation.attachments.headOption @@ -769,12 +706,12 @@ private class PresentationServiceImpl( s"No matching issued credentials found in prover db from the given: $credentialsToUse", validatedCredentialsFormat ) - count <- presentationRepository + _ <- presentationRepository .updatePresentationWithCredentialsToUse(recordId, Option(credentialsToUse), ProtocolState.PresentationPending) - .mapError(RepositoryError.apply) @@ CustomMetricsAspect.startRecordingTime( - s"${record.id}_present_proof_flow_prover_presentation_pending_to_generated_ms_gauge" - ) - record <- fetchPresentationRecord(recordId, count) + @@ CustomMetricsAspect.startRecordingTime( + s"${record.id}_present_proof_flow_prover_presentation_pending_to_generated_ms_gauge" + ) + record <- getRecord(recordId) } yield record } def acceptSDJWTRequestPresentation( @@ -792,32 +729,17 @@ private class PresentationServiceImpl( s"No matching issued credentials found in prover db from the given: $credentialsToUse", validatedCredentialsFormat ) - count <- presentationRepository - .updateSDJWTPresentationWithCredentialsToUse( - recordId, - Option(credentialsToUse), - claimsToDisclose, - ProtocolState.PresentationPending + _ <- + presentationRepository + .updateSDJWTPresentationWithCredentialsToUse( + recordId, + Option(credentialsToUse), + claimsToDisclose, + ProtocolState.PresentationPending + ) @@ CustomMetricsAspect.startRecordingTime( + s"${record.id}_present_proof_flow_prover_presentation_pending_to_generated_ms_gauge" ) - .mapError(RepositoryError.apply) @@ CustomMetricsAspect.startRecordingTime( - s"${record.id}_present_proof_flow_prover_presentation_pending_to_generated_ms_gauge" - ) - record <- fetchPresentationRecord(recordId, count) - } yield record - } - - private def fetchPresentationRecord(recordId: DidCommID, count: RuntimeFlags) = { - for { - _ <- count match - case 1 => ZIO.succeed(()) - case _ => ZIO.fail(RecordIdNotFound(recordId)) - record <- presentationRepository - .getPresentationRecord(recordId) - .mapError(RepositoryError.apply) - .flatMap { - case None => ZIO.fail(RecordIdNotFound(recordId)) - case Some(value) => ZIO.succeed(value) - } + record <- getRecord(recordId) } yield record } @@ -852,11 +774,10 @@ private class PresentationServiceImpl( Option(AnoncredCredentialProofsV1.version), Option(anoncredCredentialProofsV1AsJson), ProtocolState.PresentationPending - ) - .mapError(RepositoryError.apply) @@ CustomMetricsAspect.startRecordingTime( + ) @@ CustomMetricsAspect.startRecordingTime( s"${record.id}_present_proof_flow_prover_presentation_pending_to_generated_ms_gauge" ) - record <- fetchPresentationRecord(recordId, count) + record <- getRecord(record.id) } yield record } @@ -917,12 +838,7 @@ private class PresentationServiceImpl( recordId: DidCommID ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] = { for { - maybeRecord <- presentationRepository - .getPresentationRecord(recordId) - .mapError(RepositoryError.apply) - record <- ZIO - .fromOption(maybeRecord) - .mapError(_ => RecordIdNotFound(recordId)) + record <- getRecord(recordId) _ <- ZIO .fromOption(record.presentationData) .mapError(_ => InvalidFlowStateError(s"No request found for this record: $recordId")) @@ -936,21 +852,11 @@ private class PresentationServiceImpl( for { record <- getRecordFromThreadId(presentation.thid) _ <- presentationRepository - .updateWithPresentation(record.id, presentation, ProtocolState.PresentationReceived) - .flatMap { - case 1 => ZIO.succeed(()) - case n => ZIO.fail(UnexpectedException(s"Invalid row count result: $n")) - } - .mapError(RepositoryError.apply) @@ CustomMetricsAspect.startRecordingTime( - s"${record.id}_present_proof_flow_verifier_presentation_received_to_verification_success_or_failure_ms_gauge" - ) - record <- presentationRepository - .getPresentationRecord(record.id) - .mapError(RepositoryError.apply) - .flatMap { - case None => ZIO.fail(RecordIdNotFound(record.id)) - case Some(value) => ZIO.succeed(value) - } + .updateWithPresentation(record.id, presentation, ProtocolState.PresentationReceived) @@ CustomMetricsAspect + .startRecordingTime( + s"${record.id}_present_proof_flow_verifier_presentation_received_to_verification_success_or_failure_ms_gauge" + ) + record <- getRecord(record.id) } yield record } @@ -958,30 +864,15 @@ private class PresentationServiceImpl( recordId: DidCommID ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] = { for { - maybeRecord <- presentationRepository - .getPresentationRecord(recordId) - .mapError(RepositoryError.apply) - record <- ZIO - .fromOption(maybeRecord) - .mapError(_ => RecordIdNotFound(recordId)) + record <- getRecord(recordId) request <- ZIO .fromOption(record.proposePresentationData) .mapError(_ => InvalidFlowStateError(s"No request found for this record: $recordId")) // TODO: Generate the JWT credential and use it to create the Presentation object requestPresentation = createDidCommRequestPresentationFromProposal(request) - count <- presentationRepository + _ <- presentationRepository .updateWithRequestPresentation(recordId, requestPresentation, ProtocolState.PresentationPending) - .mapError(RepositoryError.apply) - _ <- count match - case 1 => ZIO.succeed(()) - case _ => ZIO.fail(RecordIdNotFound(recordId)) - record <- presentationRepository - .getPresentationRecord(record.id) - .mapError(RepositoryError.apply) - .flatMap { - case None => ZIO.fail(RecordIdNotFound(record.id)) - case Some(value) => ZIO.succeed(value) - } + record <- getRecord(recordId) } yield record } @@ -992,32 +883,25 @@ private class PresentationServiceImpl( record <- getRecordFromThreadId(proposePresentation.thid) _ <- presentationRepository .updateWithProposePresentation(record.id, proposePresentation, ProtocolState.ProposalReceived) - .flatMap { - case 1 => ZIO.succeed(()) - case n => ZIO.fail(UnexpectedException(s"Invalid row count result: $n")) - } - .mapError(RepositoryError.apply) - record <- presentationRepository - .getPresentationRecord(record.id) - .mapError(RepositoryError.apply) - .flatMap { - case None => ZIO.fail(RecordIdNotFound(record.id)) - case Some(value) => ZIO.succeed(value) - } + record <- getRecord(record.id) } yield record } + private def getRecord(recordId: DidCommID) = { + presentationRepository + .findPresentationRecord(recordId) + .flatMap { + case None => ZIO.fail(RecordIdNotFound(recordId)) + case Some(value) => ZIO.succeed(value) + } + } + private def getRecordWithState( recordId: DidCommID, state: ProtocolState ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] = { for { - maybeRecord <- presentationRepository - .getPresentationRecord(recordId) - .mapError(RepositoryError.apply) - record <- ZIO - .fromOption(maybeRecord) - .mapError(_ => RecordIdNotFound(recordId)) + record <- getRecord(recordId) _ <- record.protocolState match { case s if s == state => ZIO.unit case state => ZIO.fail(InvalidFlowStateError(s"Invalid protocol state for operation: $state")) @@ -1157,13 +1041,10 @@ private class PresentationServiceImpl( recordId: DidCommID, failReason: Option[String] ): ZIO[WalletAccessContext, PresentationError, Unit] = - presentationRepository - .updateAfterFail(recordId, failReason) - .mapError(RepositoryError.apply) - .flatMap { - case 1 => ZIO.unit - case n => ZIO.fail(UnexpectedError(s"Invalid number of records updated: $n")) - } + for { + _ <- getRecord(recordId) + result <- presentationRepository.updateAfterFail(recordId, failReason) + } yield result private def getRecordFromThreadId( thid: Option[String] @@ -1174,8 +1055,7 @@ private class PresentationServiceImpl( .map(DidCommID(_)) .mapError(_ => UnexpectedError("No `thid` found in Presentation request")) maybeRecord <- presentationRepository - .getPresentationRecordByThreadId(thidID) - .mapError(RepositoryError.apply) + .findPresentationRecordByThreadId(thidID) record <- ZIO .fromOption(maybeRecord) .mapError(_ => ThreadIdNotFound(thidID)) @@ -1248,18 +1128,12 @@ private class PresentationServiceImpl( id: DidCommID, from: PresentationRecord.ProtocolState, to: PresentationRecord.ProtocolState - ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] = { + ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] = for { - _ <- presentationRepository - .updatePresentationRecordProtocolState(id, from, to) - .flatMap { - case 1 => ZIO.succeed(()) - case n => ZIO.fail(UnexpectedException(s"Invalid row count result: $n")) - } - .mapError(RepositoryError.apply) - record <- fetchPresentationRecord(id, 1) + _ <- getRecord(id) + _ <- presentationRepository.updatePresentationRecordProtocolState(id, from, to) + record <- getRecord(id) } yield record - } } diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/PresentationServiceNotifier.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/PresentationServiceNotifier.scala index 07c38b97dc..57cac47c11 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/PresentationServiceNotifier.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/PresentationServiceNotifier.scala @@ -244,15 +244,15 @@ class PresentationServiceNotifier( ): IO[PresentationError, Seq[PresentationRecord]] = svc.getPresentationRecordsByStatesForAllWallets(ignoreWithZeroRetries, limit, state*) - override def getPresentationRecord( + override def findPresentationRecord( recordId: DidCommID ): ZIO[WalletAccessContext, PresentationError, Option[PresentationRecord]] = - svc.getPresentationRecord(recordId) + svc.findPresentationRecord(recordId) - override def getPresentationRecordByThreadId( + override def findPresentationRecordByThreadId( thid: DidCommID ): ZIO[WalletAccessContext, PresentationError, Option[PresentationRecord]] = - svc.getPresentationRecordByThreadId(thid) + svc.findPresentationRecordByThreadId(thid) override def receiveProposePresentation( request: ProposePresentation diff --git a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/repository/PresentationRepositorySpecSuite.scala b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/repository/PresentationRepositorySpecSuite.scala index c2d5140b54..04331c6eb5 100644 --- a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/repository/PresentationRepositorySpecSuite.scala +++ b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/repository/PresentationRepositorySpecSuite.scala @@ -61,16 +61,17 @@ object PresentationRepositorySpecSuite { for { repo <- ZIO.service[PresentationRepository] record = presentationRecord - count <- repo.createPresentationRecord(record) - } yield assertTrue(count == 1) + _ <- repo.createPresentationRecord(record) + result <- repo.findPresentationRecord(record.id) + } yield assert(result)(isSome) }, test("createPresentationRecord correctly read and write on non-null connectionId") { for { repo <- ZIO.service[PresentationRepository] record = presentationRecord.copy(connectionId = Some("connectionId")) - count <- repo.createPresentationRecord(record) - readRecord <- repo.getPresentationRecord(record.id) - } yield assertTrue(count == 1) && assert(readRecord)(isSome(equalTo(record))) + _ <- repo.createPresentationRecord(record) + readRecord <- repo.findPresentationRecord(record.id) + } yield assert(readRecord)(isSome(equalTo(record))) }, test("getPresentationRecord correctly returns an existing record") { for { @@ -79,7 +80,7 @@ object PresentationRepositorySpecSuite { bRecord = presentationRecord _ <- repo.createPresentationRecord(aRecord) _ <- repo.createPresentationRecord(bRecord) - record <- repo.getPresentationRecord(bRecord.id) + record <- repo.findPresentationRecord(bRecord.id) } yield assertTrue(record.contains(bRecord)) }, test("getPresentationRecord returns None for an unknown record") { @@ -89,7 +90,7 @@ object PresentationRepositorySpecSuite { bRecord = presentationRecord _ <- repo.createPresentationRecord(aRecord) _ <- repo.createPresentationRecord(bRecord) - record <- repo.getPresentationRecord(DidCommID()) + record <- repo.findPresentationRecord(DidCommID()) } yield assertTrue(record.isEmpty) }, test("getPresentationRecord returns all records") { @@ -114,7 +115,7 @@ object PresentationRepositorySpecSuite { bRecord = presentationRecord _ <- repo.createPresentationRecord(aRecord) _ <- repo.createPresentationRecord(bRecord) - record <- repo.getPresentationRecordByThreadId(thid) + record <- repo.findPresentationRecordByThreadId(thid) } yield assertTrue(record.contains(aRecord)) }, test("getPresentationRecordByThreadId returns nothing for an unknown thid") { @@ -124,7 +125,7 @@ object PresentationRepositorySpecSuite { bRecord = presentationRecord _ <- repo.createPresentationRecord(aRecord) _ <- repo.createPresentationRecord(bRecord) - record <- repo.getPresentationRecordByThreadId(DidCommID()) + record <- repo.findPresentationRecordByThreadId(DidCommID()) } yield assertTrue(record.isEmpty) }, test("getPresentationRecordsByStates returns valid records") { @@ -193,7 +194,7 @@ object PresentationRepositorySpecSuite { Some(Seq("credential1", "credential2")), ProtocolState.PresentationPending ) - records <- repo.getPresentationRecord(aRecord.id) + records <- repo.findPresentationRecord(aRecord.id) } yield { assertTrue(records.size == 1) && assertTrue(records.exists(_.credentialsToUse.contains(Seq("credential1", "credential2")))) @@ -220,7 +221,7 @@ object PresentationRepositorySpecSuite { Some(anoncredCredentialProofsJson), ProtocolState.PresentationPending ) - records <- repo.getPresentationRecord(aRecord.id) + records <- repo.findPresentationRecord(aRecord.id) } yield { assertTrue(records.size == 1) && assertTrue(records.exists(_.anoncredCredentialsToUse.contains(anoncredCredentialProofsJson))) && @@ -232,15 +233,14 @@ object PresentationRepositorySpecSuite { repo <- ZIO.service[PresentationRepository] aRecord = presentationRecord _ <- repo.createPresentationRecord(aRecord) - record <- repo.getPresentationRecord(aRecord.id) - count <- repo.updatePresentationRecordProtocolState( + record <- repo.findPresentationRecord(aRecord.id) + _ <- repo.updatePresentationRecordProtocolState( aRecord.id, ProtocolState.RequestPending, ProtocolState.RequestSent ) - updatedRecord <- repo.getPresentationRecord(aRecord.id) + updatedRecord <- repo.findPresentationRecord(aRecord.id) } yield { - assertTrue(count == 1) && assertTrue(record.get.protocolState == ProtocolState.RequestPending) && assertTrue(updatedRecord.get.protocolState == ProtocolState.RequestSent) } @@ -250,34 +250,30 @@ object PresentationRepositorySpecSuite { repo <- ZIO.service[PresentationRepository] aRecord = presentationRecord _ <- repo.createPresentationRecord(aRecord) - record <- repo.getPresentationRecord(aRecord.id) - count <- repo.updatePresentationRecordProtocolState( - aRecord.id, - ProtocolState.PresentationPending, - ProtocolState.RequestSent - ) - updatedRecord <- repo.getPresentationRecord(aRecord.id) - } yield { - assertTrue(count == 0) && - assertTrue(record.get.protocolState == ProtocolState.RequestPending) && - assertTrue(updatedRecord.get.protocolState == ProtocolState.RequestPending) - } + record <- repo.findPresentationRecord(aRecord.id) + exit <- repo + .updatePresentationRecordProtocolState( + aRecord.id, + ProtocolState.PresentationPending, + ProtocolState.RequestSent + ) + .exit + } yield assert(exit)(dies(hasMessage(equalTo("Unexpected affected row count: 0")))) }, test("updateWithRequestPresentation updates record") { for { repo <- ZIO.service[PresentationRepository] aRecord = presentationRecord _ <- repo.createPresentationRecord(aRecord) - record <- repo.getPresentationRecord(aRecord.id) + record <- repo.findPresentationRecord(aRecord.id) request = requestPresentation - count <- repo.updateWithRequestPresentation( + _ <- repo.updateWithRequestPresentation( aRecord.id, request, ProtocolState.RequestPending ) - updatedRecord <- repo.getPresentationRecord(aRecord.id) + updatedRecord <- repo.findPresentationRecord(aRecord.id) } yield { - assertTrue(count == 1) && assertTrue(record.get.requestPresentationData.isEmpty) && assertTrue(updatedRecord.get.requestPresentationData.contains(request)) } @@ -287,16 +283,15 @@ object PresentationRepositorySpecSuite { repo <- ZIO.service[PresentationRepository] aRecord = presentationRecord _ <- repo.createPresentationRecord(aRecord) - record <- repo.getPresentationRecord(aRecord.id) + record <- repo.findPresentationRecord(aRecord.id) presentation = Presentation.makePresentationFromRequest(requestPresentation.makeMessage) - count <- repo.updateWithPresentation( + _ <- repo.updateWithPresentation( aRecord.id, presentation, ProtocolState.PresentationPending ) - updatedRecord <- repo.getPresentationRecord(aRecord.id) + updatedRecord <- repo.findPresentationRecord(aRecord.id) } yield { - assertTrue(count == 1) && assertTrue(record.get.presentationData.isEmpty) && assertTrue(updatedRecord.get.presentationData.contains(presentation)) } @@ -306,16 +301,15 @@ object PresentationRepositorySpecSuite { repo <- ZIO.service[PresentationRepository] aRecord = presentationRecord _ <- repo.createPresentationRecord(aRecord) - record <- repo.getPresentationRecord(aRecord.id) + record <- repo.findPresentationRecord(aRecord.id) request = proposePresentation - count <- repo.updateWithProposePresentation( + _ <- repo.updateWithProposePresentation( aRecord.id, request, ProtocolState.ProposalPending ) - updatedRecord <- repo.getPresentationRecord(aRecord.id) + updatedRecord <- repo.findPresentationRecord(aRecord.id) } yield { - assertTrue(count == 1) && assertTrue(record.get.proposePresentationData.isEmpty) && assertTrue(updatedRecord.get.proposePresentationData.contains(request)) } @@ -327,24 +321,21 @@ object PresentationRepositorySpecSuite { for { repo <- ZIO.service[PresentationRepository] tmp <- repo.createPresentationRecord(aRecord) - record0 <- repo.getPresentationRecord(aRecord.id) + record0 <- repo.findPresentationRecord(aRecord.id) _ <- repo.updateAfterFail(aRecord.id, Some("Just to test")) // TEST - updatedRecord1 <- repo.getPresentationRecord(aRecord.id) - count <- repo.updatePresentationRecordProtocolState( + updatedRecord1 <- repo.findPresentationRecord(aRecord.id) + _ <- repo.updatePresentationRecordProtocolState( aRecord.id, ProtocolState.RequestPending, ProtocolState.RequestSent ) - updatedRecord2 <- repo.getPresentationRecord(aRecord.id) + updatedRecord2 <- repo.findPresentationRecord(aRecord.id) } yield { - assertTrue(tmp == 1) && assertTrue(record0.isDefined) && assertTrue(record0.get.metaRetries == maxRetries) && assertTrue(updatedRecord1.get.metaRetries == (maxRetries - 1)) && assertTrue(updatedRecord1.get.metaLastFailure == failReason) && assertTrue(updatedRecord1.get.metaNextRetry.isDefined) && - // continues to work normally after retry - assertTrue(count == 1) && assertTrue(updatedRecord2.get.metaNextRetry.isDefined) && assertTrue(updatedRecord2.get.metaRetries == maxRetries) && assertTrue(updatedRecord2.get.metaLastFailure.isEmpty) @@ -355,25 +346,18 @@ object PresentationRepositorySpecSuite { for { repo <- ZIO.service[PresentationRepository] - tmp <- repo.createPresentationRecord(aRecord) - record0 <- repo.getPresentationRecord(aRecord.id) - count1 <- repo.updateAfterFail(aRecord.id, Some("1 - Just to test")) - count2 <- repo.updateAfterFail(aRecord.id, Some("2 - Just to test")) - count3 <- repo.updateAfterFail(aRecord.id, Some("3 - Just to test")) - count4 <- repo.updateAfterFail(aRecord.id, Some("4 - Just to test")) - count5 <- repo.updateAfterFail(aRecord.id, Some("5 - Just to test")) - count6 <- repo.updateAfterFail(aRecord.id, Some("6 - Just to test")) + _ <- repo.createPresentationRecord(aRecord) + record0 <- repo.findPresentationRecord(aRecord.id) + _ <- repo.updateAfterFail(aRecord.id, Some("1 - Just to test")) + _ <- repo.updateAfterFail(aRecord.id, Some("2 - Just to test")) + _ <- repo.updateAfterFail(aRecord.id, Some("3 - Just to test")) + _ <- repo.updateAfterFail(aRecord.id, Some("4 - Just to test")) + _ <- repo.updateAfterFail(aRecord.id, Some("5 - Just to test")) + _ <- repo.updateAfterFail(aRecord.id, Some("6 - Just to test")) // The 6 retry should not happen since the max retries is 5 // (but should also not have an effect other that update the error message) - updatedRecord1 <- repo.getPresentationRecord(aRecord.id) + updatedRecord1 <- repo.findPresentationRecord(aRecord.id) } yield { - - assertTrue(tmp == 1) && - assertTrue(count1 == 1) && - assertTrue(count2 == 1) && - assertTrue(count3 == 1) && - assertTrue(count4 == 1) && - assertTrue(count5 == 1) && assertTrue(record0.isDefined) && assertTrue(record0.get.metaRetries == maxRetries) && assertTrue(updatedRecord1.get.metaRetries == 0) && // assume the max retries is 5 @@ -394,20 +378,45 @@ object PresentationRepositorySpecSuite { repo <- ZIO.service[PresentationRepository] record1 = presentationRecord record2 = presentationRecord - count1 <- repo.createPresentationRecord(record1).provide(wallet1) - count2 <- repo.createPresentationRecord(record2).provide(wallet2) + _ <- repo.createPresentationRecord(record1).provide(wallet1) + _ <- repo.createPresentationRecord(record2).provide(wallet2) ownWalletRecords1 <- repo.getPresentationRecords(false).provide(wallet1) ownWalletRecords2 <- repo.getPresentationRecords(false).provide(wallet2) - crossWalletRecordById <- repo.getPresentationRecord(record2.id).provide(wallet1) - crossWalletRecordByThid <- repo.getPresentationRecordByThreadId(record2.thid).provide(wallet1) - } yield assert(count1)(equalTo(1)) && - assert(count2)(equalTo(1)) && - assert(ownWalletRecords1)(hasSameElements(Seq(record1))) && + crossWalletRecordById <- repo.findPresentationRecord(record2.id).provide(wallet1) + crossWalletRecordByThid <- repo.findPresentationRecordByThreadId(record2.thid).provide(wallet1) + } yield assert(ownWalletRecords1)(hasSameElements(Seq(record1))) && assert(ownWalletRecords2)(hasSameElements(Seq(record2))) && assert(crossWalletRecordById)(isNone) && assert(crossWalletRecordByThid)(isNone) }, - test("unable to update PresentationRecord outside of the wallet") { + test("unable to update updatePresentationWithCredentialsToUse outside of the wallet") { + val walletId1 = WalletId.random + val walletId2 = WalletId.random + val wallet1 = ZLayer.succeed(WalletAccessContext(walletId1)) + val wallet2 = ZLayer.succeed(WalletAccessContext(walletId2)) + val newState = PresentationRecord.ProtocolState.PresentationVerified + for { + repo <- ZIO.service[PresentationRepository] + record1 = presentationRecord + record2 = presentationRecord + _ <- repo.createPresentationRecord(record1).provide(wallet1) + exit <- repo.updatePresentationWithCredentialsToUse(record2.id, Option(Nil), newState).provide(wallet2).exit + } yield assert(exit)(dies(hasMessage(equalTo("Unexpected affected row count: 0")))) + }, + test("unable to updateAfterFail PresentationRecord outside of the wallet") { + val walletId1 = WalletId.random + val walletId2 = WalletId.random + val wallet1 = ZLayer.succeed(WalletAccessContext(walletId1)) + val wallet2 = ZLayer.succeed(WalletAccessContext(walletId2)) + for { + repo <- ZIO.service[PresentationRepository] + record1 = presentationRecord + record2 = presentationRecord + _ <- repo.createPresentationRecord(record1).provide(wallet1) + exit <- repo.updateAfterFail(record2.id, Some("fail reason")).provide(wallet2).exit + } yield assert(exit)(dies(hasMessage(equalTo("Unexpected affected row count: 0")))) + }, + test("unable to updatePresentationRecordProtocolState PresentationRecord outside of the wallet") { val walletId1 = WalletId.random val walletId2 = WalletId.random val wallet1 = ZLayer.succeed(WalletAccessContext(walletId1)) @@ -417,16 +426,12 @@ object PresentationRepositorySpecSuite { repo <- ZIO.service[PresentationRepository] record1 = presentationRecord record2 = presentationRecord - count1 <- repo.createPresentationRecord(record1).provide(wallet1) - update1 <- repo.updatePresentationWithCredentialsToUse(record2.id, Option(Nil), newState).provide(wallet2) - update2 <- repo.updateAfterFail(record2.id, Some("fail reason")).provide(wallet2) - update3 <- repo + _ <- repo.createPresentationRecord(record1).provide(wallet1) + exit <- repo .updatePresentationRecordProtocolState(record2.id, record1.protocolState, newState) .provide(wallet2) - } yield assert(count1)(equalTo(1)) && - assert(update1)(isZero) && - assert(update2)(isZero) && - assert(update3)(isZero) + .exit + } yield assert(exit)(dies(hasMessage(equalTo("Unexpected affected row count: 0")))) }, test("getPresentationRecordsByStatesForAllWallets should return all the records") { val walletId1 = WalletId.random @@ -437,8 +442,8 @@ object PresentationRepositorySpecSuite { repo <- ZIO.service[PresentationRepository] record1 = presentationRecord record2 = presentationRecord - count1 <- repo.createPresentationRecord(record1).provide(wallet1) - count2 <- repo.createPresentationRecord(record2).provide(wallet2) + _ <- repo.createPresentationRecord(record1).provide(wallet1) + _ <- repo.createPresentationRecord(record2).provide(wallet2) _ <- repo .updatePresentationRecordProtocolState( record1.id, @@ -459,9 +464,7 @@ object PresentationRepositorySpecSuite { ProtocolState.RequestSent, ProtocolState.PresentationReceived ) - } yield assert(count1)(equalTo(1)) && - assert(count2)(equalTo(1)) && - assertTrue(allRecords.size == 2) && + } yield assertTrue(allRecords.size == 2) && assertTrue(allRecords.exists(_.id == record1.id)) && assertTrue(allRecords.exists(_.id == record2.id)) }, diff --git a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/PresentationServiceSpec.scala b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/PresentationServiceSpec.scala index 5788721e7f..b1ff6d356b 100644 --- a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/PresentationServiceSpec.scala +++ b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/PresentationServiceSpec.scala @@ -210,7 +210,7 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp svc <- ZIO.service[PresentationService] _ <- svc.createJwtRecord() bRecord <- svc.createJwtRecord() - record <- svc.getPresentationRecord(bRecord.id) + record <- svc.findPresentationRecord(bRecord.id) } yield assertTrue(record.contains(bRecord)) }, test("getPresentationRecord returns nothing for an unknown 'recordId'") { @@ -218,7 +218,7 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp svc <- ZIO.service[PresentationService] _ <- svc.createJwtRecord() _ <- svc.createJwtRecord() - record <- svc.getPresentationRecord(DidCommID()) + record <- svc.findPresentationRecord(DidCommID()) } yield assertTrue(record.isEmpty) }, test("createJwtPresentationPayloadFromRecord returns jwt presentation payload") { @@ -747,11 +747,6 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp p = proposePresentation(aRecord.thid.value) aRecordReceived <- svc.receiveProposePresentation(p) repo <- ZIO.service[PresentationRepository] - _ <- repo.updatePresentationRecordProtocolState( - aRecord.id, - PresentationRecord.ProtocolState.ProposalPending, - PresentationRecord.ProtocolState.ProposalReceived - ) _ <- svc.acceptProposePresentation(aRecord.id) } yield { assertTrue(aRecordReceived.id == aRecord.id) && @@ -916,10 +911,10 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp svc <- ZIO.service[PresentationService] record1 <- svc.createJwtRecord().provide(wallet1) record2 <- svc.createJwtRecord().provide(wallet2) - ownRecord1 <- svc.getPresentationRecord(record1.id).provide(wallet1) - ownRecord2 <- svc.getPresentationRecord(record2.id).provide(wallet2) - crossRecord1 <- svc.getPresentationRecord(record1.id).provide(wallet2) - crossRecord2 <- svc.getPresentationRecord(record2.id).provide(wallet1) + ownRecord1 <- svc.findPresentationRecord(record1.id).provide(wallet1) + ownRecord2 <- svc.findPresentationRecord(record2.id).provide(wallet2) + crossRecord1 <- svc.findPresentationRecord(record1.id).provide(wallet2) + crossRecord2 <- svc.findPresentationRecord(record2.id).provide(wallet1) } yield assert(ownRecord1)(isSome(equalTo(record1))) && assert(ownRecord2)(isSome(equalTo(record2))) && assert(crossRecord1)(isNone) && diff --git a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcPresentationRepository.scala b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcPresentationRepository.scala index 16e51df8ae..c7c3ea323c 100644 --- a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcPresentationRepository.scala +++ b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcPresentationRepository.scala @@ -37,7 +37,7 @@ class JdbcPresentationRepository( recordId: DidCommID, credentialsToUse: Option[Seq[String]], protocolState: ProtocolState - ): RIO[WalletAccessContext, Int] = { + ): URIO[WalletAccessContext, Unit] = { val cxnIO = sql""" | UPDATE public.presentation_records | SET @@ -53,6 +53,8 @@ class JdbcPresentationRepository( cxnIO.run .transactWallet(xa) + .orDie + .ensureOneAffectedRowOrDie } override def updateSDJWTPresentationWithCredentialsToUse( @@ -60,7 +62,7 @@ class JdbcPresentationRepository( credentialsToUse: Option[Seq[String]], sdJwtClaimsToDisclose: Option[SdJwtCredentialToDisclose], protocolState: ProtocolState - ): RIO[WalletAccessContext, Int] = { + ): URIO[WalletAccessContext, Unit] = { val cxnIO = sql""" | UPDATE public.presentation_records @@ -78,6 +80,8 @@ class JdbcPresentationRepository( cxnIO.run .transactWallet(xa) + .orDie + .ensureOneAffectedRowOrDie } def updateAnoncredPresentationWithCredentialsToUse( @@ -85,7 +89,7 @@ class JdbcPresentationRepository( anoncredCredentialsToUseJsonSchemaId: Option[String], anoncredCredentialsToUse: Option[AnoncredCredentialProofs], protocolState: ProtocolState - ): RIO[WalletAccessContext, Int] = { + ): URIO[WalletAccessContext, Unit] = { val cxnIO = sql""" | UPDATE public.presentation_records @@ -103,6 +107,8 @@ class JdbcPresentationRepository( cxnIO.run .transactWallet(xa) + .orDie + .ensureOneAffectedRowOrDie } // Uncomment to have Doobie LogHandler in scope and automatically output SQL statements in logs @@ -156,7 +162,7 @@ class JdbcPresentationRepository( Get[String].map(decode[ProposePresentation](_).getOrElse(???)) given proposePresentationPut: Put[ProposePresentation] = Put[String].contramap(_.asJson.toString) - override def createPresentationRecord(record: PresentationRecord): RIO[WalletAccessContext, Int] = { + override def createPresentationRecord(record: PresentationRecord): URIO[WalletAccessContext, Unit] = { val cxnIO = sql""" | INSERT INTO public.presentation_records( | id, @@ -205,11 +211,13 @@ class JdbcPresentationRepository( cxnIO.run .transactWallet(xa) + .orDie + .ensureOneAffectedRowOrDie } override def getPresentationRecords( ignoreWithZeroRetries: Boolean - ): RIO[WalletAccessContext, Seq[PresentationRecord]] = { + ): URIO[WalletAccessContext, Seq[PresentationRecord]] = { val conditionFragment = Fragments.whereAndOpt( Option.when(ignoreWithZeroRetries)(fr"meta_retries > 0") ) @@ -246,6 +254,7 @@ class JdbcPresentationRepository( cxnIO .transactWallet(xa) + .orDie } private def getRecordsByStates( @@ -299,18 +308,18 @@ class JdbcPresentationRepository( ignoreWithZeroRetries: Boolean, limit: Int, states: PresentationRecord.ProtocolState* - ): RIO[WalletAccessContext, Seq[PresentationRecord]] = { - getRecordsByStates(ignoreWithZeroRetries, limit, states*).transactWallet(xa) + ): URIO[WalletAccessContext, Seq[PresentationRecord]] = { + getRecordsByStates(ignoreWithZeroRetries, limit, states*).transactWallet(xa).orDie } override def getPresentationRecordsByStatesForAllWallets( ignoreWithZeroRetries: Boolean, limit: Int, states: PresentationRecord.ProtocolState* - ): Task[Seq[PresentationRecord]] = { - getRecordsByStates(ignoreWithZeroRetries, limit, states*).transact(xb) + ): UIO[Seq[PresentationRecord]] = { + getRecordsByStates(ignoreWithZeroRetries, limit, states*).transact(xb).orDie } - override def getPresentationRecord(recordId: DidCommID): RIO[WalletAccessContext, Option[PresentationRecord]] = { + override def findPresentationRecord(recordId: DidCommID): URIO[WalletAccessContext, Option[PresentationRecord]] = { val cxnIO = sql""" | SELECT | id, @@ -342,11 +351,12 @@ class JdbcPresentationRepository( cxnIO .transactWallet(xa) + .orDie } - override def getPresentationRecordByThreadId( + override def findPresentationRecordByThreadId( thid: DidCommID - ): RIO[WalletAccessContext, Option[PresentationRecord]] = { + ): URIO[WalletAccessContext, Option[PresentationRecord]] = { val cxnIO = sql""" | SELECT | id, @@ -378,13 +388,14 @@ class JdbcPresentationRepository( cxnIO .transactWallet(xa) + .orDie } override def updatePresentationRecordProtocolState( recordId: DidCommID, from: PresentationRecord.ProtocolState, to: PresentationRecord.ProtocolState - ): RIO[WalletAccessContext, Int] = { + ): URIO[WalletAccessContext, Unit] = { val cxnIO = sql""" | UPDATE public.presentation_records | SET @@ -400,13 +411,15 @@ class JdbcPresentationRepository( cxnIO.run .transactWallet(xa) + .orDie + .ensureOneAffectedRowOrDie } override def updateWithRequestPresentation( recordId: DidCommID, request: RequestPresentation, protocolState: ProtocolState - ): RIO[WalletAccessContext, Int] = { + ): URIO[WalletAccessContext, Unit] = { val cxnIO = sql""" | UPDATE public.presentation_records | SET @@ -422,13 +435,15 @@ class JdbcPresentationRepository( cxnIO.run .transactWallet(xa) + .orDie + .ensureOneAffectedRowOrDie } override def updateWithProposePresentation( recordId: DidCommID, propose: ProposePresentation, protocolState: ProtocolState - ): RIO[WalletAccessContext, Int] = { + ): URIO[WalletAccessContext, Unit] = { val cxnIO = sql""" | UPDATE public.presentation_records | SET @@ -444,13 +459,15 @@ class JdbcPresentationRepository( cxnIO.run .transactWallet(xa) + .orDie + .ensureOneAffectedRowOrDie } override def updateWithPresentation( recordId: DidCommID, presentation: Presentation, protocolState: ProtocolState - ): RIO[WalletAccessContext, Int] = { + ): URIO[WalletAccessContext, Unit] = { val cxnIO = sql""" | UPDATE public.presentation_records | SET @@ -466,12 +483,14 @@ class JdbcPresentationRepository( cxnIO.run .transactWallet(xa) + .orDie + .ensureOneAffectedRowOrDie } def updateAfterFail( recordId: DidCommID, failReason: Option[String] - ): RIO[WalletAccessContext, Int] = { + ): URIO[WalletAccessContext, Unit] = { val cxnIO = sql""" | UPDATE public.presentation_records | SET @@ -481,7 +500,10 @@ class JdbcPresentationRepository( | WHERE | id = $recordId """.stripMargin.update - cxnIO.run.transactWallet(xa) + cxnIO.run + .transactWallet(xa) + .orDie + .ensureOneAffectedRowOrDie } }