diff --git a/build.sbt b/build.sbt index 7a5348bf34..38a9a2a404 100644 --- a/build.sbt +++ b/build.sbt @@ -34,7 +34,7 @@ inThisBuild( "-unchecked", "-Dquill.macro.log=false", // disable quill macro logs "-Wunused:all", - "-Wconf:any:warning" // TODO: change unused imports to errors, Wconf configuration string is different from scala 2, figure out how! + "-Wconf:any:warning", // TODO: change unused imports to errors, Wconf configuration string is different from scala 2, figure out how! // TODO "-feature", // TODO "-Xfatal-warnings", // TODO "-Yexplicit-nulls", @@ -502,7 +502,8 @@ lazy val models = project ), // TODO try to remove this from this module // libraryDependencies += D.didScala ) - .settings(libraryDependencies += D.nimbusJwt) //FIXME just for the DidAgent + .settings(libraryDependencies += D.nimbusJwt) // FIXME just for the DidAgent + .dependsOn(shared) /* TODO move code from agentDidcommx to here models implementation for didcommx () */ diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/BackgroundJobError.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/BackgroundJobError.scala index 8cd4be569d..3ac9e438b9 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/BackgroundJobError.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/BackgroundJobError.scala @@ -1,15 +1,40 @@ package org.hyperledger.identus.agent.server.jobs import org.hyperledger.identus.mercury.HttpResponse +import org.hyperledger.identus.shared.models._ -sealed trait BackgroundJobError +sealed trait BackgroundJobError( + override val statusCode: org.hyperledger.identus.shared.models.StatusCode, + override val userFacingMessage: String +) extends Failure { + override val namespace: String = "BackgroundJobError" +} object BackgroundJobError { - final case class InvalidState(cause: String) extends BackgroundJobError - final case class ErrorSendingMessage(cause: String) extends BackgroundJobError - case object NotImplemented extends BackgroundJobError - final case class ErrorResponseReceivedFromPeerAgent(response: HttpResponse) extends BackgroundJobError { + final case class InvalidState(cause: String) + extends BackgroundJobError( + statusCode = StatusCode.InternalServerError, + userFacingMessage = s"Invalid State: cause='$cause'" + ) + + final case class ErrorSendingMessage(cause: String) + extends BackgroundJobError( + statusCode = StatusCode.BadRequest, + userFacingMessage = s"ErrorSendingMessage" + ) + + case object NotImplemented + extends BackgroundJobError( + statusCode = StatusCode.UnexpectedNotImplemented, + userFacingMessage = s"NotImplemented" + ) + final case class ErrorResponseReceivedFromPeerAgent(response: HttpResponse) + extends BackgroundJobError( + statusCode = StatusCode.BadRequest, + userFacingMessage = s"DIDComm sending error: [${response.status}] - ${response.bodyAsString}" + ) { + override def toString: String = s"DIDComm sending error: [${response.status}] - ${response.bodyAsString}" } } diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/ConnectBackgroundJobs.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/ConnectBackgroundJobs.scala index 483a7a6d87..5ed92b4441 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/ConnectBackgroundJobs.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/ConnectBackgroundJobs.scala @@ -6,10 +6,13 @@ import org.hyperledger.identus.agent.walletapi.model.error.DIDSecretStorageError import org.hyperledger.identus.agent.walletapi.model.error.DIDSecretStorageError.{KeyNotFoundError, WalletNotFoundError} import org.hyperledger.identus.agent.walletapi.service.ManagedDIDService import org.hyperledger.identus.agent.walletapi.storage.DIDNonSecretStorage +import org.hyperledger.identus.connect.core.model.error.ConnectionServiceError.InvalidStateForOperation +import org.hyperledger.identus.connect.core.model.error.ConnectionServiceError.RecordIdNotFound import org.hyperledger.identus.connect.core.model.ConnectionRecord import org.hyperledger.identus.connect.core.model.ConnectionRecord.* import org.hyperledger.identus.connect.core.service.ConnectionService import org.hyperledger.identus.mercury.* +import org.hyperledger.identus.mercury.model.error.SendMessageError import org.hyperledger.identus.resolvers.DIDResolver import org.hyperledger.identus.shared.models.WalletAccessContext import org.hyperledger.identus.shared.utils.aspects.CustomMetricsAspect @@ -182,11 +185,11 @@ object ConnectBackgroundJobs extends BackgroundJobsHelper { s"Connect - Error processing record: ${record.id}", Cause.fail(walletNotFound) ) - case ((walletAccessContext, e)) => + case ((walletAccessContext, errorResponse)) => for { connectService <- ZIO.service[ConnectionService] _ <- connectService - .reportProcessingFailure(record.id, Some(e.toString)) + .reportProcessingFailure(record.id, Some(errorResponse)) .provideSomeLayer(ZLayer.succeed(walletAccessContext)) } yield () }) diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/IssueBackgroundJobs.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/IssueBackgroundJobs.scala index 0780309a28..d85a275e74 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/IssueBackgroundJobs.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/IssueBackgroundJobs.scala @@ -9,6 +9,7 @@ import org.hyperledger.identus.mercury.protocol.issuecredential.* import org.hyperledger.identus.pollux.core.model.* import org.hyperledger.identus.pollux.core.model.error.CredentialServiceError import org.hyperledger.identus.pollux.core.service.CredentialService +import org.hyperledger.identus.shared.models.Failure import org.hyperledger.identus.shared.utils.aspects.CustomMetricsAspect import org.hyperledger.identus.shared.utils.DurationOps.toMetricsSeconds import zio.* @@ -618,11 +619,12 @@ object IssueBackgroundJobs extends BackgroundJobsHelper { case walletNotFound: WalletNotFoundError => ZIO.unit case CredentialServiceError.RecordNotFound(_, _) => ZIO.unit case CredentialServiceError.UnsupportedDidFormat(_) => ZIO.unit - case ((walletAccessContext, e)) => + case failure: Failure => ??? // FIXME + case ((walletAccessContext, failure)) => for { credentialService <- ZIO.service[CredentialService] _ <- credentialService - .reportProcessingFailure(record.id, Some(e.toString)) + .reportProcessingFailure(record.id, Some(failure)) .provideSomeLayer(ZLayer.succeed(walletAccessContext)) } yield () } diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/PresentBackgroundJobs.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/PresentBackgroundJobs.scala index f2a3d50280..b89cd14d2a 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/PresentBackgroundJobs.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/PresentBackgroundJobs.scala @@ -17,7 +17,6 @@ import org.hyperledger.identus.castor.core.model.error.DIDResolutionError as Cas import org.hyperledger.identus.castor.core.service.DIDService import org.hyperledger.identus.mercury.* import org.hyperledger.identus.mercury.model.* -import org.hyperledger.identus.mercury.model.error.TransportError import org.hyperledger.identus.mercury.protocol.presentproof.* import org.hyperledger.identus.mercury.protocol.reportproblem.v2.{ProblemCode, ReportProblem} import org.hyperledger.identus.pollux.core.model.* @@ -30,7 +29,7 @@ import org.hyperledger.identus.pollux.sdjwt.{HolderPrivateKey, IssuerPublicKey, import org.hyperledger.identus.pollux.vc.jwt.{DidResolver as JwtDidResolver, Issuer as JwtIssuer, JWT, JwtPresentation} import org.hyperledger.identus.resolvers.DIDResolver import org.hyperledger.identus.shared.http.* -import org.hyperledger.identus.shared.models.WalletAccessContext +import org.hyperledger.identus.shared.models.* import org.hyperledger.identus.shared.utils.aspects.CustomMetricsAspect import org.hyperledger.identus.shared.utils.DurationOps.toMetricsSeconds import zio.* @@ -38,13 +37,15 @@ import zio.json.* import zio.json.ast.Json import zio.metrics.* import zio.prelude.Validation -import zio.prelude.ZValidation.* +import zio.prelude.ZValidation.{Failure => ZFailure, *} import java.time.{Clock, Instant, ZoneId} object PresentBackgroundJobs extends BackgroundJobsHelper { - type ERROR = DIDSecretStorageError | PresentationError | CredentialServiceError | BackgroundJobError | - CastorDIDResolutionError | GetManagedDIDError | TransportError + + private type ERROR = + /*DIDSecretStorageError | PresentationError | CredentialServiceError | BackgroundJobError | TransportError | */ + CastorDIDResolutionError | GetManagedDIDError | Failure private type RESOURCES = COMMON_RESOURCES & CredentialService & JwtDidResolver & DIDService & AppConfig & MESSAGING_RESOURCES @@ -82,7 +83,7 @@ object PresentBackgroundJobs extends BackgroundJobsHelper { aux(record) .tapError({ (error: PresentationError | DIDSecretStorageError | BackgroundJobError | CredentialServiceError | - CastorDIDResolutionError | GetManagedDIDError | TransportError) => + CastorDIDResolutionError | GetManagedDIDError | Failure) => ZIO.logErrorCause( s"Present Proof - Error processing record: ${record.id}", Cause.fail(error) @@ -533,7 +534,7 @@ object PresentBackgroundJobs extends BackgroundJobsHelper { requestPresentation: RequestPresentation ): ZIO[ PresentationService & DIDNonSecretStorage, - PresentationError | DIDSecretStorageError | BackgroundJobError, + Failure, Unit ] = { maybeCredentialsToUseJson match { @@ -645,7 +646,7 @@ object PresentBackgroundJobs extends BackgroundJobsHelper { def handlePresentationGenerated(id: DidCommID, presentation: Presentation): ZIO[ JwtDidResolver & COMMON_RESOURCES & MESSAGING_RESOURCES, - PresentationError | DIDSecretStorageError | BackgroundJobError | TransportError, + Failure, Unit ] = { @@ -712,7 +713,7 @@ object PresentBackgroundJobs extends BackgroundJobsHelper { def handleRequestPending(id: DidCommID, record: RequestPresentation): ZIO[ JwtDidResolver & COMMON_RESOURCES & MESSAGING_RESOURCES, - PresentationError | DIDSecretStorageError | BackgroundJobError | TransportError, + Failure, Unit ] = { val VerifierSendPresentationRequestMsgSuccess = counterMetric( @@ -800,7 +801,7 @@ object PresentBackgroundJobs extends BackgroundJobsHelper { credentialFormat: CredentialFormat ): ZIO[ AppConfig & JwtDidResolver & COMMON_RESOURCES & MESSAGING_RESOURCES, - PresentationError | DIDSecretStorageError | TransportError, + Failure, Unit ] = { val result = @@ -814,7 +815,7 @@ object PresentBackgroundJobs extends BackgroundJobsHelper { private def handleJWT(id: DidCommID, requestPresentation: RequestPresentation, presentation: Presentation): ZIO[ AppConfig & JwtDidResolver & COMMON_RESOURCES & MESSAGING_RESOURCES, - PresentationError | DIDSecretStorageError | TransportError, + Failure, Unit ] = { val clock = java.time.Clock.system(ZoneId.systemDefault) @@ -887,8 +888,8 @@ object PresentBackgroundJobs extends BackgroundJobsHelper { case any => ZIO.fail(PresentationReceivedError("Only Base64 Supported")) } _ <- credentialsClaimsValidationResult match - case l @ Failure(_, _) => ZIO.logError(s"CredentialsClaimsValidationResult: $l") - case l @ Success(_, _) => ZIO.logInfo(s"CredentialsClaimsValidationResult: $l") + case l @ ZFailure(_, _) => ZIO.logError(s"CredentialsClaimsValidationResult: $l") + case l @ Success(_, _) => ZIO.logInfo(s"CredentialsClaimsValidationResult: $l") service <- ZIO.service[PresentationService] presReceivedToProcessedAspect = CustomMetricsAspect.endRecordingTime( s"${id}_present_proof_flow_verifier_presentation_received_to_verification_success_or_failure_ms_gauge", @@ -899,7 +900,7 @@ object PresentBackgroundJobs extends BackgroundJobsHelper { service .markPresentationVerified(id) .provideSomeLayer(ZLayer.succeed(walletAccessContext)) @@ presReceivedToProcessedAspect - case Failure(log, error) => + case ZFailure(log, error) => for { _ <- service .markPresentationVerificationFailed(id) @@ -928,7 +929,7 @@ object PresentBackgroundJobs extends BackgroundJobsHelper { private def handleSDJWT(id: DidCommID, presentation: Presentation): ZIO[ JwtDidResolver & COMMON_RESOURCES & MESSAGING_RESOURCES, - PresentationError | DIDSecretStorageError | TransportError, + Failure, Unit ] = { for { @@ -997,7 +998,7 @@ object PresentBackgroundJobs extends BackgroundJobsHelper { presentation: Presentation ): ZIO[ PresentationService & DIDNonSecretStorage & MESSAGING_RESOURCES, - PresentationError | DIDSecretStorageError | TransportError, + PresentationError | DIDSecretStorageError, Unit ] = { for { diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/didcomm/controller/DIDCommControllerError.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/didcomm/controller/DIDCommControllerError.scala index 88e2f5b659..2c46434de9 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/didcomm/controller/DIDCommControllerError.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/didcomm/controller/DIDCommControllerError.scala @@ -3,49 +3,39 @@ package org.hyperledger.identus.didcomm.controller import org.hyperledger.identus.mercury.model.DidId import org.hyperledger.identus.shared.models.{Failure, StatusCode} -sealed trait DIDCommControllerError( - val statusCode: StatusCode, - val userFacingMessage: String -) extends Failure { - override val namespace = "DIDCommControllerError" +sealed trait DIDCommControllerError extends Failure { + override def namespace = "DIDCommControllerError" } object DIDCommControllerError { - final case class InvalidContentType(maybeContentType: Option[String]) - extends DIDCommControllerError( - StatusCode.BadRequest, - maybeContentType match - case Some(value) => s"The 'content-type' request header value is invalid: $value" - case None => s"The 'content-type' request header is undefined" - ) - - final case class RecipientNotFoundError() - extends DIDCommControllerError( - StatusCode.UnprocessableContent, - "Recipient not found in the DIDComm Message" - ) - - final case class UnexpectedError(override val statusCode: StatusCode) - extends DIDCommControllerError( - statusCode, - "An unexpected error occurred while processing your request" - ) - - final case class RequestBodyParsingError(cause: String) - extends DIDCommControllerError( - StatusCode.BadRequest, - s"Unable to parse the request body as a valid DIDComm message: $cause" - ) - - final case class PeerDIDNotFoundError(did: DidId) - extends DIDCommControllerError( - StatusCode.UnprocessableContent, - s"The Peer DID was not found in this agent: ${did.value}" - ) - - final case class PeerDIDKeyNotFoundError(did: DidId, keyId: String) - extends DIDCommControllerError( - StatusCode.UnprocessableContent, - s"The Peer DID does not contain the required key: DID=$did, keyId=$keyId" - ) + final case class InvalidContentType(maybeContentType: Option[String]) extends DIDCommControllerError { + override def statusCode: StatusCode = StatusCode.BadRequest + override def userFacingMessage: String = maybeContentType match + case Some(value) => s"The 'content-type' request header value is invalid: $value" + case None => s"The 'content-type' request header is undefined" + } + + object RecipientNotFoundError extends DIDCommControllerError { + override def statusCode: StatusCode = StatusCode.UnprocessableContent + override def userFacingMessage: String = "Recipient not found in the DIDComm Message" + } + + final case class UnexpectedError(statusCode: StatusCode) extends DIDCommControllerError { + override def userFacingMessage: String = "An unexpected error occurred while processing your request" + } + + final case class RequestBodyParsingError(cause: String) extends DIDCommControllerError { + override def statusCode: StatusCode = StatusCode.BadRequest + override def userFacingMessage: String = s"Unable to parse the request body as a valid DIDComm message: $cause" + } + + final case class PeerDIDNotFoundError(did: DidId) extends DIDCommControllerError { + override def statusCode: StatusCode = StatusCode.UnprocessableContent + override def userFacingMessage: String = s"The Peer DID was not found in this agent: ${did.value}" + } + + final case class PeerDIDKeyNotFoundError(did: DidId, keyId: String) extends DIDCommControllerError { + override def statusCode: StatusCode = StatusCode.UnprocessableContent + override def userFacingMessage: String = s"The Peer DID does not contain the required key: DID=$did, keyId=$keyId" + } } diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/didcomm/controller/DIDCommControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/didcomm/controller/DIDCommControllerImpl.scala index 44d7e1e8e5..b910ace0f4 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/didcomm/controller/DIDCommControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/didcomm/controller/DIDCommControllerImpl.scala @@ -71,7 +71,7 @@ class DIDCommControllerImpl( for { recipientDid <- ZIO .fromOption(msg.recipients.headOption.map(_.header.kid.split("#")(0))) - .mapError(_ => RecipientNotFoundError()) + .mapError(_ => RecipientNotFoundError) _ <- ZIO.logInfo(s"Extracted recipient Did => $recipientDid") didId = DidId(recipientDid) maybePeerDIDRecord <- didNonSecretStorage.getPeerDIDRecord(didId).orDie diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/controller/CredentialIssuerController.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/controller/CredentialIssuerController.scala index 158bdda0d7..29d81e4321 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/controller/CredentialIssuerController.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/controller/CredentialIssuerController.scala @@ -166,8 +166,9 @@ case class CredentialIssuerControllerImpl( ZIO.unit, ZIO.fail(OIDCCredentialIssuerService.Errors.InvalidProof("Invalid proof")) ) - .mapError { case InvalidProof(message) => - badRequestInvalidProof(jwt, message) + .mapError { + case InvalidProof(message) => badRequestInvalidProof(jwt, message) + case DIDResolutionError(message) => badRequestInvalidProof(jwt, message) } nonce <- getNonceFromJwt(JWT(jwt)) .mapError(throwable => badRequestInvalidProof(jwt, throwable.getMessage)) @@ -180,7 +181,9 @@ case class CredentialIssuerControllerImpl( sessionWithSubjectDid <- credentialIssuerService .updateIssuanceSession(session.withSubjectDid(subjectDid)) .mapError(ue => - serverError(Some(s"Unexpected error while updating issuance session with subject DID: ${ue.message}")) + serverError( + Some(s"Unexpected error while updating issuance session with subject DID: ${ue.userFacingMessage}") + ) ) credentialDefinition <- ZIO .fromOption(maybeCredentialDefinition) @@ -188,7 +191,7 @@ case class CredentialIssuerControllerImpl( validatedCredentialDefinition <- credentialIssuerService .validateCredentialDefinition(credentialDefinition) .mapError(ue => - serverError(Some(s"Unexpected error while validating credential definition: ${ue.message}")) + serverError(Some(s"Unexpected error while validating credential definition: ${ue.userFacingMessage}")) ) credential <- credentialIssuerService .issueJwtCredential( @@ -198,9 +201,11 @@ case class CredentialIssuerControllerImpl( maybeCredentialIdentifier, validatedCredentialDefinition ) - .mapError(ue => serverError(Some(s"Unexpected error while issuing credential: ${ue.message}"))) + .mapError(ue => serverError(Some(s"Unexpected error while issuing credential: ${ue.userFacingMessage}"))) } yield ImmediateCredentialResponse(credential.value) case None => ZIO.fail(badRequestInvalidProof(jwt = "empty", details = "No proof provided")) + // case Some(CwtProof(_, _)) => ??? // TODO + // case Some(LdpProof(_, _)) => ??? // TODO } } @@ -223,7 +228,9 @@ case class CredentialIssuerControllerImpl( ) .map(offer => CredentialOfferResponse(offer.offerUri)) .mapError(ue => - internalServerError(detail = Some(s"Unexpected error while creating credential offer: ${ue.message}")) + internalServerError(detail = + Some(s"Unexpected error while creating credential offer: ${ue.userFacingMessage}") + ) ) } yield resp } @@ -236,7 +243,7 @@ case class CredentialIssuerControllerImpl( .getIssuanceSessionByIssuerState(request.issuerState) .map(session => NonceResponse(session.nonce)) .mapError(ue => - internalServerError(detail = Some(s"Unexpected error while creating credential offer: ${ue.message}")) + internalServerError(detail = Some(s"Unexpected error while creating credential offer: ${ue.userFacingMessage}")) ) } diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/domain/Openid4VCIProofJwtOps.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/domain/Openid4VCIProofJwtOps.scala index efa527df7b..5834ebad98 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/domain/Openid4VCIProofJwtOps.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/domain/Openid4VCIProofJwtOps.scala @@ -5,7 +5,7 @@ import org.hyperledger.identus.castor.core.model.did.DIDUrl import org.hyperledger.identus.pollux.vc.jwt.JWT import org.hyperledger.identus.pollux.vc.jwt.JwtSignerImplicits.* import org.hyperledger.identus.shared.crypto.Secp256k1PrivateKey -import zio.{Task, ZIO} +import zio.* import java.util.UUID import scala.jdk.CollectionConverters.* @@ -46,36 +46,42 @@ trait Openid4VCIProofJwtOps { JWT(makeJwtProof(header, payload, privateKey.asJwtSigner)) } - def getKeyIdFromJwt(jwt: JWT): Task[String] = { + def getKeyIdFromJwt(jwt: JWT): IO[RuntimeException, String] = { // FIXME RuntimeException for { - jwsObject <- ZIO.fromTry(Try(JWSObject.parse(jwt.value))) + jwsObject <- ZIO.fromTry(Try(JWSObject.parse(jwt.value))).orDie // FIXME keyID = jwsObject.getHeader.getKeyID - _ <- ZIO.fail(new Exception("Key ID not found in JWT header")) when (keyID == null || keyID.isEmpty) + _ <- ZIO.fail( + new RuntimeException("Key ID not found in JWT header") + ) when (keyID == null || keyID.isEmpty) } yield keyID } - def getAlgorithmFromJwt(jwt: JWT): Task[String] = { + def getAlgorithmFromJwt(jwt: JWT): IO[RuntimeException, String] = { for { - jwsObject <- ZIO.fromTry(Try(JWSObject.parse(jwt.value))) + jwsObject <- ZIO.fromTry(Try(JWSObject.parse(jwt.value))).orDie // FIXME algorithm <- ZIO .fromOption(Option(jwsObject.getHeader.getAlgorithm)) - .mapError(_ => new Exception("Algorithm not found in JWT header")) + .mapError(_ => new RuntimeException("Algorithm not found in JWT header")) } yield algorithm.getName } - def getNonceFromJwt(jwt: JWT): Task[String] = { + def getNonceFromJwt(jwt: JWT): IO[RuntimeException, String] = { // FIXME RuntimeException for { - jwsObject <- ZIO.fromTry(Try(JWSObject.parse(jwt.value))) + jwsObject <- ZIO.fromTry(Try(JWSObject.parse(jwt.value))).orDie // FIXME payload = jwsObject.getPayload.toJSONObject nonce = payload.get("nonce").asInstanceOf[String] - _ <- ZIO.fail(new Exception("Nonce not found in JWT payload")) when (nonce == null || nonce.isEmpty) + _ <- ZIO.fail( + new RuntimeException("Nonce not found in JWT payload") + ) when (nonce == null || nonce.isEmpty) } yield nonce } - def parseDIDUrlFromKeyId(jwt: JWT): Task[DIDUrl] = { + def parseDIDUrlFromKeyId(jwt: JWT): IO[RuntimeException, DIDUrl] = { // FIXME RuntimeException for { keyId <- getKeyIdFromJwt(jwt) - didUrl <- ZIO.fromEither(DIDUrl.fromString(keyId)).mapError(e => new Exception(e)) + didUrl <- ZIO + .fromEither(DIDUrl.fromString(keyId)) + .mapError(e => new RuntimeException(e)) } yield didUrl } } diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/service/OIDCCredentialIssuerService.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/service/OIDCCredentialIssuerService.scala index cee750b525..bb90856cb0 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/service/OIDCCredentialIssuerService.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/service/OIDCCredentialIssuerService.scala @@ -24,7 +24,7 @@ import org.hyperledger.identus.pollux.vc.jwt.{ W3cCredentialPayload, * } -import org.hyperledger.identus.shared.models.{WalletAccessContext, WalletId} +import org.hyperledger.identus.shared.models.* import zio.* import java.net.{URI, URL} @@ -40,7 +40,7 @@ trait OIDCCredentialIssuerService { import OIDCCredentialIssuerService.Error import OIDCCredentialIssuerService.Errors.* - def verifyJwtProof(jwt: JWT): IO[InvalidProof, Boolean] + def verifyJwtProof(jwt: JWT): IO[InvalidProof | DIDResolutionError, Boolean] def validateCredentialDefinition( credentialDefinition: CredentialDefinition @@ -70,30 +70,31 @@ trait OIDCCredentialIssuerService { } object OIDCCredentialIssuerService { - trait Error { - def message: String + sealed trait Error extends Failure { + override val namespace = "OIDCCredentialIssuerService" + override val statusCode = StatusCode.BadRequest } // TODO: use shared Failure trait object Errors { - case class InvalidProof(message: String) extends Error + case class InvalidProof(userFacingMessage: String) extends Error - case class DIDResolutionError(message: String) extends Error + case class DIDResolutionError(userFacingMessage: String) extends Error case class CredentialConfigurationNotFound(issuerId: UUID, credentialConfigurationId: String) extends Error { - override def message: String = + override def userFacingMessage: String = s"Credential configuration with id $credentialConfigurationId not found for issuer $issuerId" } case class CredentialSchemaError(cause: org.hyperledger.identus.pollux.core.model.error.CredentialSchemaError) extends Error { - override def message: String = cause.userFacingMessage + override def userFacingMessage: String = cause.userFacingMessage } - case class ServiceError(message: String) extends Error + case class ServiceError(userFacingMessage: String) extends Error case class UnexpectedError(cause: Throwable) extends Error { - override def message: String = cause.getMessage + override def userFacingMessage: String = cause.getMessage // TODO } } } @@ -129,14 +130,13 @@ case class OIDCCredentialIssuerServiceImpl( } yield verificationMethod } - override def verifyJwtProof(jwt: JWT): IO[InvalidProof, Boolean] = { + override def verifyJwtProof(jwt: JWT): IO[InvalidProof | DIDResolutionError, Boolean] = { for { algorithm <- getAlgorithmFromJwt(jwt) .mapError(e => InvalidProof(e.getMessage)) didUrl <- parseDIDUrlFromKeyId(jwt) .mapError(e => InvalidProof(e.getMessage)) verificationMethod <- resolveVerificationMethodByKeyId(didUrl) - .mapError(dre => InvalidProof(dre.message)) publicKey <- JWTVerification.extractPublicKey(verificationMethod).toZIO.mapError(InvalidProof.apply) _ <- JWTVerification.validateEncodedJwt(jwt, publicKey).toZIO.mapError(InvalidProof.apply) } yield true diff --git a/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/model/error/DIDSecretStorageError.scala b/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/model/error/DIDSecretStorageError.scala index 779d8fbde0..85a305541e 100644 --- a/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/model/error/DIDSecretStorageError.scala +++ b/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/model/error/DIDSecretStorageError.scala @@ -1,10 +1,24 @@ package org.hyperledger.identus.agent.walletapi.model.error import org.hyperledger.identus.mercury.model.DidId +import org.hyperledger.identus.shared.models._ -sealed trait DIDSecretStorageError extends Throwable +sealed trait DIDSecretStorageError( + override val statusCode: StatusCode, + override val userFacingMessage: String +) extends Failure { + override val namespace: String = "DIDSecretStorageError" +} object DIDSecretStorageError { - case class KeyNotFoundError(didId: DidId, keyId: String) extends DIDSecretStorageError - case class WalletNotFoundError(didId: DidId) extends DIDSecretStorageError + case class KeyNotFoundError(didId: DidId, keyId: String) + extends DIDSecretStorageError( + StatusCode.NotFound, + s"The not found: keyId='$keyId', didId='$didId'" + ) + case class WalletNotFoundError(didId: DidId) + extends DIDSecretStorageError( + StatusCode.NotFound, + s"The DID not Found in Wallet: didId='$didId'" + ) } diff --git a/connect/core/src/main/scala/org/hyperledger/identus/connect/core/model/ConnectionRecord.scala b/connect/core/src/main/scala/org/hyperledger/identus/connect/core/model/ConnectionRecord.scala index 13eccd9c81..db640624d2 100644 --- a/connect/core/src/main/scala/org/hyperledger/identus/connect/core/model/ConnectionRecord.scala +++ b/connect/core/src/main/scala/org/hyperledger/identus/connect/core/model/ConnectionRecord.scala @@ -3,6 +3,7 @@ package org.hyperledger.identus.connect.core.model import org.hyperledger.identus.connect.core.model.ConnectionRecord.{ProtocolState, Role} import org.hyperledger.identus.mercury.protocol.connection.{ConnectionRequest, ConnectionResponse} import org.hyperledger.identus.mercury.protocol.invitation.v2.Invitation +import org.hyperledger.identus.shared.models.Failure import java.time.temporal.ChronoUnit import java.time.Instant @@ -42,7 +43,7 @@ case class ConnectionRecord( connectionResponse: Option[ConnectionResponse], metaRetries: Int, metaNextRetry: Option[Instant], - metaLastFailure: Option[String] + metaLastFailure: Option[Failure] ) { def withTruncatedTimestamp(unit: ChronoUnit = ChronoUnit.MICROS): ConnectionRecord = copy( createdAt = createdAt.truncatedTo(unit), diff --git a/connect/core/src/main/scala/org/hyperledger/identus/connect/core/repository/ConnectionRepository.scala b/connect/core/src/main/scala/org/hyperledger/identus/connect/core/repository/ConnectionRepository.scala index 8407d4f751..9ad70b3160 100644 --- a/connect/core/src/main/scala/org/hyperledger/identus/connect/core/repository/ConnectionRepository.scala +++ b/connect/core/src/main/scala/org/hyperledger/identus/connect/core/repository/ConnectionRepository.scala @@ -3,6 +3,7 @@ package org.hyperledger.identus.connect.core.repository import org.hyperledger.identus.connect.core.model.ConnectionRecord import org.hyperledger.identus.connect.core.model.ConnectionRecord.ProtocolState import org.hyperledger.identus.mercury.protocol.connection.* +import org.hyperledger.identus.shared.models.Failure import org.hyperledger.identus.shared.models.WalletAccessContext import zio.{UIO, URIO} @@ -67,7 +68,7 @@ trait ConnectionRepository { def updateAfterFail( recordId: UUID, - failReason: Option[String], + failReason: Option[Failure], ): URIO[WalletAccessContext, Unit] } diff --git a/connect/core/src/main/scala/org/hyperledger/identus/connect/core/repository/ConnectionRepositoryInMemory.scala b/connect/core/src/main/scala/org/hyperledger/identus/connect/core/repository/ConnectionRepositoryInMemory.scala index 0ebb7d0a8c..316abdc6c3 100644 --- a/connect/core/src/main/scala/org/hyperledger/identus/connect/core/repository/ConnectionRepositoryInMemory.scala +++ b/connect/core/src/main/scala/org/hyperledger/identus/connect/core/repository/ConnectionRepositoryInMemory.scala @@ -3,7 +3,7 @@ package org.hyperledger.identus.connect.core.repository import org.hyperledger.identus.connect.core.model.ConnectionRecord import org.hyperledger.identus.connect.core.model.ConnectionRecord.ProtocolState import org.hyperledger.identus.mercury.protocol.connection.{ConnectionRequest, ConnectionResponse} -import org.hyperledger.identus.shared.models.{WalletAccessContext, WalletId} +import org.hyperledger.identus.shared.models.* import zio.* import java.time.Instant @@ -113,7 +113,7 @@ class ConnectionRepositoryInMemory(walletRefs: Ref[Map[WalletId, Ref[Map[UUID, C def updateAfterFail( recordId: UUID, - failReason: Option[String], + failReason: Option[Failure], ): URIO[WalletAccessContext, Unit] = for { maybeRecord <- findById(recordId) record <- ZIO.getOrFailWith(new RuntimeException(s"Record not found for Id: $recordId"))(maybeRecord).orDie @@ -131,7 +131,7 @@ class ConnectionRepositoryInMemory(walletRefs: Ref[Map[WalletId, Ref[Map[UUID, C def updateAfterFailForAllWallets( recordId: UUID, - failReason: Option[String], + failReason: Option[Failure], ): Task[Int] = walletRefs.get.flatMap { wallets => ZIO.foldLeft(wallets.values)(0) { (acc, walletRef) => for { diff --git a/connect/core/src/main/scala/org/hyperledger/identus/connect/core/service/ConnectionService.scala b/connect/core/src/main/scala/org/hyperledger/identus/connect/core/service/ConnectionService.scala index 8cab80087f..9b4409bb9a 100644 --- a/connect/core/src/main/scala/org/hyperledger/identus/connect/core/service/ConnectionService.scala +++ b/connect/core/src/main/scala/org/hyperledger/identus/connect/core/service/ConnectionService.scala @@ -5,7 +5,7 @@ import org.hyperledger.identus.connect.core.model.error.ConnectionServiceError.* import org.hyperledger.identus.connect.core.model.ConnectionRecord import org.hyperledger.identus.mercury.model.DidId import org.hyperledger.identus.mercury.protocol.connection.{ConnectionRequest, ConnectionResponse} -import org.hyperledger.identus.shared.models.WalletAccessContext +import org.hyperledger.identus.shared.models.* import zio.* import java.time.Duration @@ -86,6 +86,6 @@ trait ConnectionService { def reportProcessingFailure( recordId: UUID, - failReason: Option[String] + failReason: Option[Failure] ): URIO[WalletAccessContext, Unit] } diff --git a/connect/core/src/main/scala/org/hyperledger/identus/connect/core/service/ConnectionServiceImpl.scala b/connect/core/src/main/scala/org/hyperledger/identus/connect/core/service/ConnectionServiceImpl.scala index 08f4bafb53..cd703af4b1 100644 --- a/connect/core/src/main/scala/org/hyperledger/identus/connect/core/service/ConnectionServiceImpl.scala +++ b/connect/core/src/main/scala/org/hyperledger/identus/connect/core/service/ConnectionServiceImpl.scala @@ -9,7 +9,7 @@ import org.hyperledger.identus.connect.core.repository.ConnectionRepository import org.hyperledger.identus.mercury.model.DidId import org.hyperledger.identus.mercury.protocol.connection.* import org.hyperledger.identus.mercury.protocol.invitation.v2.Invitation -import org.hyperledger.identus.shared.models.WalletAccessContext +import org.hyperledger.identus.shared.models.* import org.hyperledger.identus.shared.utils.aspects.CustomMetricsAspect import org.hyperledger.identus.shared.utils.Base64Utils import org.hyperledger.identus.shared.validation.ValidationUtils @@ -295,9 +295,9 @@ private class ConnectionServiceImpl( } yield record } - def reportProcessingFailure( + override def reportProcessingFailure( recordId: UUID, - failReason: Option[String] + failReason: Option[Failure] ): URIO[WalletAccessContext, Unit] = connectionRepository.updateAfterFail(recordId, failReason) diff --git a/connect/core/src/main/scala/org/hyperledger/identus/connect/core/service/ConnectionServiceNotifier.scala b/connect/core/src/main/scala/org/hyperledger/identus/connect/core/service/ConnectionServiceNotifier.scala index 4aeec45ac5..8037fb1ed8 100644 --- a/connect/core/src/main/scala/org/hyperledger/identus/connect/core/service/ConnectionServiceNotifier.scala +++ b/connect/core/src/main/scala/org/hyperledger/identus/connect/core/service/ConnectionServiceNotifier.scala @@ -6,7 +6,7 @@ import org.hyperledger.identus.connect.core.model.ConnectionRecord import org.hyperledger.identus.event.notification.{Event, EventNotificationService} import org.hyperledger.identus.mercury.model.DidId import org.hyperledger.identus.mercury.protocol.connection.{ConnectionRequest, ConnectionResponse} -import org.hyperledger.identus.shared.models.WalletAccessContext +import org.hyperledger.identus.shared.models.* import zio.{UIO, URIO, URLayer, ZIO, ZLayer} import java.time.Duration @@ -103,7 +103,7 @@ class ConnectionServiceNotifier( override def reportProcessingFailure( recordId: UUID, - failReason: Option[String] + failReason: Option[Failure] ): URIO[WalletAccessContext, Unit] = svc.reportProcessingFailure(recordId, failReason) diff --git a/connect/core/src/main/scala/org/hyperledger/identus/connect/core/service/MockConnectionService.scala b/connect/core/src/main/scala/org/hyperledger/identus/connect/core/service/MockConnectionService.scala index ddcc93828e..9d30a2abde 100644 --- a/connect/core/src/main/scala/org/hyperledger/identus/connect/core/service/MockConnectionService.scala +++ b/connect/core/src/main/scala/org/hyperledger/identus/connect/core/service/MockConnectionService.scala @@ -5,7 +5,7 @@ import org.hyperledger.identus.connect.core.model.error.ConnectionServiceError.* import org.hyperledger.identus.connect.core.model.ConnectionRecord import org.hyperledger.identus.mercury.model.DidId import org.hyperledger.identus.mercury.protocol.connection.{ConnectionRequest, ConnectionResponse} -import org.hyperledger.identus.shared.models.WalletAccessContext +import org.hyperledger.identus.shared.models.* import zio.{mock, UIO, URIO, URLayer, ZIO, ZLayer} import zio.mock.{Mock, Proxy} @@ -126,7 +126,7 @@ object MockConnectionService extends Mock[ConnectionService] { override def reportProcessingFailure( recordId: UUID, - failReason: Option[String] + failReason: Option[Failure] ): URIO[WalletAccessContext, Unit] = ??? } } diff --git a/connect/core/src/test/scala/org/hyperledger/identus/connect/core/repository/ConnectionRepositorySpecSuite.scala b/connect/core/src/test/scala/org/hyperledger/identus/connect/core/repository/ConnectionRepositorySpecSuite.scala index acb1226c64..ac7b1a6f6c 100644 --- a/connect/core/src/test/scala/org/hyperledger/identus/connect/core/repository/ConnectionRepositorySpecSuite.scala +++ b/connect/core/src/test/scala/org/hyperledger/identus/connect/core/repository/ConnectionRepositorySpecSuite.scala @@ -5,6 +5,7 @@ import org.hyperledger.identus.connect.core.model.ConnectionRecord.* import org.hyperledger.identus.mercury.model.DidId import org.hyperledger.identus.mercury.protocol.connection.{ConnectionRequest, ConnectionResponse} import org.hyperledger.identus.mercury.protocol.invitation.v2.Invitation +import org.hyperledger.identus.shared.models.* import org.hyperledger.identus.shared.models.{WalletAccessContext, WalletId} import zio.{Cause, Exit, ZIO, ZLayer} import zio.test.* @@ -335,13 +336,16 @@ object ConnectionRepositorySpecSuite { } }, test("updateFail (fail one retry) updates record") { - val failReason = Some("Just to test") + val failReason = Some(FailureInfo("ConnectionRepositorySpecSuite", StatusCode(999), "Just to test")) for { repo <- ZIO.service[ConnectionRepository] aRecord = connectionRecord _ <- repo.create(aRecord) record <- repo.findById(aRecord.id) - count <- repo.updateAfterFail(aRecord.id, Some("Just to test")) // TEST + count <- repo.updateAfterFail( + aRecord.id, + Some(FailureInfo("ConnectionRepositorySpecSuite", StatusCode(999), "Just to test")) + ) // TEST updatedRecord1 <- repo.findById(aRecord.id) response = ConnectionResponse.makeResponseFromRequest(connectionRequest.makeMessage).toOption.get _ <- repo.updateWithConnectionResponse( diff --git a/connect/sql-doobie/src/main/resources/sql/connect/V8__clear_content_of_meta_last_failure.sql b/connect/sql-doobie/src/main/resources/sql/connect/V8__clear_content_of_meta_last_failure.sql new file mode 100644 index 0000000000..bf1eddfb59 --- /dev/null +++ b/connect/sql-doobie/src/main/resources/sql/connect/V8__clear_content_of_meta_last_failure.sql @@ -0,0 +1,2 @@ +-- Clear content of meta_last_failure +UPDATE public.connection_records SET meta_last_failure=NULL; \ No newline at end of file diff --git a/connect/sql-doobie/src/main/scala/org/hyperledger/identus/connect/sql/repository/JdbcConnectionRepository.scala b/connect/sql-doobie/src/main/scala/org/hyperledger/identus/connect/sql/repository/JdbcConnectionRepository.scala index def56b6df9..18d8a1ac05 100644 --- a/connect/sql-doobie/src/main/scala/org/hyperledger/identus/connect/sql/repository/JdbcConnectionRepository.scala +++ b/connect/sql-doobie/src/main/scala/org/hyperledger/identus/connect/sql/repository/JdbcConnectionRepository.scala @@ -15,9 +15,10 @@ import org.hyperledger.identus.mercury.protocol.connection.* import org.hyperledger.identus.mercury.protocol.invitation.v2.Invitation import org.hyperledger.identus.shared.db.ContextAwareTask import org.hyperledger.identus.shared.db.Implicits.* -import org.hyperledger.identus.shared.models.WalletAccessContext +import org.hyperledger.identus.shared.models.* import zio.* import zio.interop.catz.* +import zio.json.* import java.time.Instant import java.util.UUID @@ -45,6 +46,9 @@ class JdbcConnectionRepository(xa: Transactor[ContextAwareTask], xb: Transactor[ given connectionResponseGet: Get[ConnectionResponse] = Get[String].map(decode[ConnectionResponse](_).getOrElse(???)) given connectionResponsePut: Put[ConnectionResponse] = Put[String].contramap(_.asJson.toString) + given failureGet: Get[Failure] = Get[String].temap(_.fromJson[FailureInfo]) + given failurePut: Put[Failure] = Put[String].contramap(_.asFailureInfo.toJson) + override def create(record: ConnectionRecord): URIO[WalletAccessContext, Unit] = { val cxnIO = sql""" | INSERT INTO public.connection_records( @@ -327,7 +331,7 @@ class JdbcConnectionRepository(xa: Transactor[ContextAwareTask], xb: Transactor[ def updateAfterFail( recordId: UUID, - failReason: Option[String], + failReason: Option[Failure], ): URIO[WalletAccessContext, Unit] = { val cxnIO = sql""" | UPDATE public.connection_records diff --git a/mercury/models/src/main/scala/org/hyperledger/identus/mercury/model/error/package.scala b/mercury/models/src/main/scala/org/hyperledger/identus/mercury/model/error/package.scala index 8d0b39acf0..4ea83a1357 100644 --- a/mercury/models/src/main/scala/org/hyperledger/identus/mercury/model/error/package.scala +++ b/mercury/models/src/main/scala/org/hyperledger/identus/mercury/model/error/package.scala @@ -1,11 +1,18 @@ package org.hyperledger.identus.mercury.model -package object error { +import org.hyperledger.identus.shared.models._ - sealed trait MercuryError +package object error { - trait TransportError extends MercuryError + sealed trait MercuryError extends Failure { + override val namespace: String = "MercuryError" + } - sealed case class SendMessageError(cause: Throwable, mData: Option[String] = None) extends TransportError + sealed case class SendMessageError(cause: Throwable, mData: Option[String] = None) extends MercuryError { + override val statusCode = StatusCode.BadRequest + override val userFacingMessage = + s"Error when sending message: ${cause.getMessage};${mData.map(e => s" DATA:'$e'").getOrElse("")}. " + + cause.getMessage + } } diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/IssueCredentialRecord.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/IssueCredentialRecord.scala index 030fa2865a..108d7c2a1d 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/IssueCredentialRecord.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/IssueCredentialRecord.scala @@ -11,7 +11,7 @@ import org.hyperledger.identus.mercury.protocol.issuecredential.{ } import org.hyperledger.identus.pollux.anoncreds.AnoncredCredentialRequestMetadata import org.hyperledger.identus.pollux.core.model.IssueCredentialRecord.* -import org.hyperledger.identus.shared.models.KeyId +import org.hyperledger.identus.shared.models.* import java.time.temporal.ChronoUnit import java.time.Instant @@ -40,7 +40,7 @@ final case class IssueCredentialRecord( issuingDID: Option[CanonicalPrismDID], metaRetries: Int, metaNextRetry: Option[Instant], - metaLastFailure: Option[String], + metaLastFailure: Option[Failure], ) { def offerCredentialFormatAndData: Option[(IssueCredentialOfferFormat, OfferCredential)] = offerCredentialData.map { data => diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/PresentationRecord.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/PresentationRecord.scala index 92ba7a493c..6847451cfd 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/PresentationRecord.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/PresentationRecord.scala @@ -2,6 +2,7 @@ package org.hyperledger.identus.pollux.core.model import org.hyperledger.identus.mercury.model.DidId import org.hyperledger.identus.mercury.protocol.presentproof.{Presentation, ProposePresentation, RequestPresentation} +import org.hyperledger.identus.shared.models.Failure import java.time.temporal.ChronoUnit import java.time.Instant @@ -30,7 +31,7 @@ final case class PresentationRecord( sdJwtClaimsToDisclose: Option[SdJwtCredentialToDisclose], metaRetries: Int, metaNextRetry: Option[Instant], - metaLastFailure: Option[String], + metaLastFailure: Option[Failure], ) { def withTruncatedTimestamp(unit: ChronoUnit = ChronoUnit.MICROS): PresentationRecord = copy( diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialRepository.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialRepository.scala index 713c34ce8c..2ac6c75c7a 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialRepository.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialRepository.scala @@ -4,7 +4,7 @@ import org.hyperledger.identus.mercury.protocol.issuecredential.{IssueCredential import org.hyperledger.identus.pollux.anoncreds.AnoncredCredentialRequestMetadata import org.hyperledger.identus.pollux.core.model.* import org.hyperledger.identus.pollux.core.model.IssueCredentialRecord.ProtocolState -import org.hyperledger.identus.shared.models.{KeyId, WalletAccessContext} +import org.hyperledger.identus.shared.models.* import zio.* trait CredentialRepository { @@ -98,7 +98,7 @@ trait CredentialRepository { def updateAfterFail( recordId: DidCommID, - failReason: Option[String] + failReason: Option[Failure] ): URIO[WalletAccessContext, Unit] } diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialRepositoryInMemory.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialRepositoryInMemory.scala index cb1594262d..aa7d5390f2 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialRepositoryInMemory.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialRepositoryInMemory.scala @@ -4,7 +4,7 @@ import org.hyperledger.identus.mercury.protocol.issuecredential.{IssueCredential import org.hyperledger.identus.pollux.anoncreds.AnoncredCredentialRequestMetadata import org.hyperledger.identus.pollux.core.model.* import org.hyperledger.identus.pollux.core.model.IssueCredentialRecord.ProtocolState -import org.hyperledger.identus.shared.models.{KeyId, WalletAccessContext, WalletId} +import org.hyperledger.identus.shared.models.* import zio.* import java.time.Instant @@ -327,9 +327,9 @@ class CredentialRepositoryInMemory( } yield () } - def updateAfterFail( + override def updateAfterFail( recordId: DidCommID, - failReason: Option[String] + failReason: Option[Failure] ): URIO[WalletAccessContext, Unit] = for { storeRef <- walletStoreRef maybeRecord <- findById(recordId) 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 2e80f3af54..789a25473d 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 @@ -3,7 +3,7 @@ package org.hyperledger.identus.pollux.core.repository import org.hyperledger.identus.mercury.protocol.presentproof.* import org.hyperledger.identus.pollux.core.model.* import org.hyperledger.identus.pollux.core.model.PresentationRecord.ProtocolState -import org.hyperledger.identus.shared.models.WalletAccessContext +import org.hyperledger.identus.shared.models.* import zio.* trait PresentationRepository { @@ -73,6 +73,6 @@ trait PresentationRepository { def updateAfterFail( recordId: DidCommID, - failReason: Option[String] + failReason: Option[Failure] ): 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 c1194838bb..4f646f3bee 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 @@ -4,7 +4,7 @@ import org.hyperledger.identus.mercury.protocol.presentproof.* import org.hyperledger.identus.pollux.core.model.* import org.hyperledger.identus.pollux.core.model.PresentationRecord.ProtocolState import org.hyperledger.identus.shared.db.Implicits.* -import org.hyperledger.identus.shared.models.{WalletAccessContext, WalletId} +import org.hyperledger.identus.shared.models.* import zio.* import zio.json.* import zio.json.ast.Json @@ -324,9 +324,9 @@ class PresentationRepositoryInMemory( result.ensureOneAffectedRowOrDie } - def updateAfterFail( + override def updateAfterFail( recordId: DidCommID, - failReason: Option[String] + failReason: Option[Failure] ): URIO[WalletAccessContext, Unit] = { val result = for { diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialService.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialService.scala index f2430246e4..047748a844 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialService.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialService.scala @@ -14,7 +14,7 @@ import org.hyperledger.identus.pollux.core.model.* import org.hyperledger.identus.pollux.core.model.error.CredentialServiceError import org.hyperledger.identus.pollux.core.model.error.CredentialServiceError.* import org.hyperledger.identus.pollux.vc.jwt.Issuer -import org.hyperledger.identus.shared.models.{KeyId, WalletAccessContext} +import org.hyperledger.identus.shared.models.* import zio.{Duration, IO, UIO, URIO, ZIO} import java.nio.charset.StandardCharsets @@ -149,7 +149,7 @@ trait CredentialService { def reportProcessingFailure( recordId: DidCommID, - failReason: Option[String] + failReason: Option[Failure] ): URIO[WalletAccessContext, Unit] def getJwtIssuer( diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceImpl.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceImpl.scala index 3cf773fa07..c0af18ac02 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceImpl.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceImpl.scala @@ -24,7 +24,7 @@ import org.hyperledger.identus.pollux.sdjwt.* import org.hyperledger.identus.pollux.vc.jwt.{Issuer as JwtIssuer, *} import org.hyperledger.identus.shared.crypto.{Ed25519KeyPair, Ed25519PublicKey, Secp256k1KeyPair} import org.hyperledger.identus.shared.http.{DataUrlResolver, GenericUriResolver} -import org.hyperledger.identus.shared.models.{KeyId, WalletAccessContext} +import org.hyperledger.identus.shared.models.* import org.hyperledger.identus.shared.utils.aspects.CustomMetricsAspect import zio.* import zio.json.* @@ -857,7 +857,7 @@ class CredentialServiceImpl( override def reportProcessingFailure( recordId: DidCommID, - failReason: Option[String] + failReason: Option[Failure] ): URIO[WalletAccessContext, Unit] = credentialRepository.updateAfterFail(recordId, failReason) diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceNotifier.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceNotifier.scala index 42599e6b2f..ab1ecf2e8c 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceNotifier.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceNotifier.scala @@ -9,7 +9,7 @@ import org.hyperledger.identus.pollux.core.model.{DidCommID, IssueCredentialReco import org.hyperledger.identus.pollux.core.model.error.CredentialServiceError import org.hyperledger.identus.pollux.core.model.error.CredentialServiceError.* import org.hyperledger.identus.pollux.vc.jwt.Issuer -import org.hyperledger.identus.shared.models.{KeyId, WalletAccessContext} +import org.hyperledger.identus.shared.models.* import zio.{Duration, UIO, URIO, URLayer, ZIO, ZLayer} import java.util.UUID @@ -187,7 +187,7 @@ class CredentialServiceNotifier( override def reportProcessingFailure( recordId: DidCommID, - failReason: Option[_root_.java.lang.String] + failReason: Option[Failure] ): URIO[WalletAccessContext, Unit] = svc.reportProcessingFailure(recordId, failReason) diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/MockCredentialService.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/MockCredentialService.scala index 1ee2264c0c..29533eb4d1 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/MockCredentialService.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/MockCredentialService.scala @@ -8,7 +8,7 @@ import org.hyperledger.identus.pollux.core.model.{DidCommID, IssueCredentialReco import org.hyperledger.identus.pollux.core.model.error.CredentialServiceError import org.hyperledger.identus.pollux.core.model.error.CredentialServiceError.* import org.hyperledger.identus.pollux.vc.jwt.Issuer -import org.hyperledger.identus.shared.models.{KeyId, WalletAccessContext} +import org.hyperledger.identus.shared.models.* import zio.{mock, Duration, IO, UIO, URIO, URLayer, ZIO, ZLayer} import zio.mock.{Mock, Proxy} @@ -99,7 +99,7 @@ object MockCredentialService extends Mock[CredentialService] { object MarkCredentialPublicationPending extends Effect[DidCommID, CredentialServiceError, IssueCredentialRecord] object MarkCredentialPublicationQueued extends Effect[DidCommID, CredentialServiceError, IssueCredentialRecord] object MarkCredentialPublished extends Effect[DidCommID, CredentialServiceError, IssueCredentialRecord] - object ReportProcessingFailure extends Effect[(DidCommID, Option[String]), Nothing, Unit] + object ReportProcessingFailure extends Effect[(DidCommID, Option[Failure]), Nothing, Unit] override val compose: URLayer[mock.Proxy, CredentialService] = ZLayer { for { @@ -244,7 +244,7 @@ object MockCredentialService extends Mock[CredentialService] { override def reportProcessingFailure( recordId: DidCommID, - failReason: Option[String] + failReason: Option[Failure] ): URIO[WalletAccessContext, Unit] = proxy(ReportProcessingFailure, recordId, failReason) 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 b1dbadc82e..36a68da0bb 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 @@ -14,7 +14,7 @@ import org.hyperledger.identus.pollux.core.model.presentation.Options import org.hyperledger.identus.pollux.core.service.serdes.{AnoncredCredentialProofsV1, AnoncredPresentationRequestV1} import org.hyperledger.identus.pollux.sdjwt.{HolderPrivateKey, PresentationCompact} import org.hyperledger.identus.pollux.vc.jwt.{Issuer, PresentationPayload, W3cCredentialPayload} -import org.hyperledger.identus.shared.models.WalletAccessContext +import org.hyperledger.identus.shared.models.* import zio.{mock, IO, URLayer, ZIO, ZLayer} import zio.json.* import zio.mock.{Mock, Proxy} @@ -261,7 +261,7 @@ object MockPresentationService extends Mock[PresentationService] { override def reportProcessingFailure( recordId: DidCommID, - failReason: Option[String] + failReason: Option[Failure] ): IO[PresentationError, Unit] = ??? } 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 f7b0fe9b09..6038278a1c 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 @@ -9,7 +9,7 @@ import org.hyperledger.identus.pollux.core.model.presentation.* import org.hyperledger.identus.pollux.core.service.serdes.{AnoncredCredentialProofsV1, AnoncredPresentationRequestV1} import org.hyperledger.identus.pollux.sdjwt.{HolderPrivateKey, PresentationCompact} import org.hyperledger.identus.pollux.vc.jwt.* -import org.hyperledger.identus.shared.models.WalletAccessContext +import org.hyperledger.identus.shared.models.* import zio.* import zio.json.ast @@ -168,7 +168,7 @@ trait PresentationService { def reportProcessingFailure( recordId: DidCommID, - failReason: Option[String] + failReason: Option[Failure] ): ZIO[WalletAccessContext, PresentationError, Unit] } 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 d153000be6..acdc2adc06 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 @@ -18,7 +18,7 @@ import org.hyperledger.identus.pollux.core.repository.{CredentialRepository, Pre import org.hyperledger.identus.pollux.core.service.serdes.* import org.hyperledger.identus.pollux.sdjwt.{CredentialCompact, HolderPrivateKey, PresentationCompact, SDJWT} import org.hyperledger.identus.pollux.vc.jwt.* -import org.hyperledger.identus.shared.models.WalletAccessContext +import org.hyperledger.identus.shared.models.* import org.hyperledger.identus.shared.utils.aspects.CustomMetricsAspect import zio.* import zio.json.* @@ -1100,7 +1100,7 @@ private class PresentationServiceImpl( def reportProcessingFailure( recordId: DidCommID, - failReason: Option[String] + failReason: Option[Failure] ): ZIO[WalletAccessContext, PresentationError, Unit] = for { _ <- getRecord(recordId) 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 41fd7636cc..c8558054c0 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 @@ -15,7 +15,7 @@ import org.hyperledger.identus.pollux.core.model.presentation.Options import org.hyperledger.identus.pollux.core.service.serdes.{AnoncredCredentialProofsV1, AnoncredPresentationRequestV1} import org.hyperledger.identus.pollux.sdjwt.{HolderPrivateKey, PresentationCompact} import org.hyperledger.identus.pollux.vc.jwt.{Issuer, PresentationPayload, W3cCredentialPayload} -import org.hyperledger.identus.shared.models.WalletAccessContext +import org.hyperledger.identus.shared.models.* import zio.{IO, URLayer, ZIO, ZLayer} import zio.json.* @@ -280,7 +280,7 @@ class PresentationServiceNotifier( override def reportProcessingFailure( recordId: DidCommID, - failReason: Option[_root_.java.lang.String] + failReason: Option[Failure] ): ZIO[WalletAccessContext, PresentationError, Unit] = svc.reportProcessingFailure(recordId, failReason) } diff --git a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/repository/CredentialRepositorySpecSuite.scala b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/repository/CredentialRepositorySpecSuite.scala index 68eda6c637..68e27c307d 100644 --- a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/repository/CredentialRepositorySpecSuite.scala +++ b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/repository/CredentialRepositorySpecSuite.scala @@ -5,7 +5,7 @@ import org.hyperledger.identus.mercury.model.DidId import org.hyperledger.identus.mercury.protocol.issuecredential.{IssueCredential, RequestCredential} import org.hyperledger.identus.pollux.core.model.* import org.hyperledger.identus.pollux.core.model.IssueCredentialRecord.* -import org.hyperledger.identus.shared.models.{KeyId, WalletAccessContext, WalletId} +import org.hyperledger.identus.shared.models.* import zio.{Exit, ZIO, ZLayer} import zio.test.* import zio.test.Assertion.* @@ -388,12 +388,15 @@ object CredentialRepositorySpecSuite { test("updateFail (fail one retry) updates record") { val aRecord = issueCredentialRecord(CredentialFormat.JWT) - val failReason = Some("Just to test") + val failReason = FailureInfo("CredentialRepositorySpecSuite", StatusCode(999), "Just to test") for { repo <- ZIO.service[CredentialRepository] _ <- repo.create(aRecord) record0 <- repo.findById(aRecord.id) - _ <- repo.updateAfterFail(aRecord.id, Some("Just to test")) // TEST + _ <- repo.updateAfterFail( + aRecord.id, + Some(FailureInfo("CredentialRepositorySpecSuite", StatusCode(999), "Just to test")) + ) updatedRecord1 <- repo.findById(aRecord.id) _ <- repo.updateProtocolState(aRecord.id, ProtocolState.OfferPending, ProtocolState.OfferSent) updatedRecord2 <- repo.findById(aRecord.id) @@ -401,7 +404,7 @@ object CredentialRepositorySpecSuite { assertTrue(record0.isDefined) && assertTrue(record0.get.metaRetries == maxRetries) && assertTrue(updatedRecord1.get.metaRetries == (maxRetries - 1)) && - assertTrue(updatedRecord1.get.metaLastFailure == failReason) && + assertTrue(updatedRecord1.get.metaLastFailure.get == failReason) && assertTrue(updatedRecord1.get.metaNextRetry.isDefined) && // continues to work normally after retry assertTrue(updatedRecord2.get.metaNextRetry.isDefined) && @@ -416,12 +419,30 @@ object CredentialRepositorySpecSuite { repo <- ZIO.service[CredentialRepository] _ <- repo.create(aRecord) record0 <- repo.findById(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")) + _ <- repo.updateAfterFail( + aRecord.id, + Some(FailureInfo("CredentialRepositorySpecSuite", StatusCode(999), "1 - Just to test")) + ) + _ <- repo.updateAfterFail( + aRecord.id, + Some(FailureInfo("CredentialRepositorySpecSuite", StatusCode(999), "2 - Just to test")) + ) + _ <- repo.updateAfterFail( + aRecord.id, + Some(FailureInfo("CredentialRepositorySpecSuite", StatusCode(999), "3 - Just to test")) + ) + _ <- repo.updateAfterFail( + aRecord.id, + Some(FailureInfo("CredentialRepositorySpecSuite", StatusCode(999), "4 - Just to test")) + ) + _ <- repo.updateAfterFail( + aRecord.id, + Some(FailureInfo("CredentialRepositorySpecSuite", StatusCode(999), "5 - Just to test")) + ) + _ <- repo.updateAfterFail( + aRecord.id, + Some(FailureInfo("CredentialRepositorySpecSuite", StatusCode(999), "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.findById(aRecord.id) @@ -431,7 +452,11 @@ object CredentialRepositorySpecSuite { assertTrue(record0.get.metaRetries == maxRetries) && assertTrue(updatedRecord1.get.metaRetries == 0) && // assume the max retries is 5 assertTrue(updatedRecord1.get.metaNextRetry.isEmpty) && - assertTrue(updatedRecord1.get.metaLastFailure == Some("6 - Just to test")) + assertTrue( + updatedRecord1.get.metaLastFailure == Some( + FailureInfo("CredentialRepositorySpecSuite", StatusCode(999), "6 - Just to test") + ) + ) } } 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 04331c6eb5..47bd53dda6 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 @@ -5,7 +5,7 @@ import org.hyperledger.identus.mercury.protocol.presentproof.{Presentation, Prop import org.hyperledger.identus.pollux.core.model.* import org.hyperledger.identus.pollux.core.model.PresentationRecord.* import org.hyperledger.identus.pollux.core.service.serdes.{AnoncredCredentialProofV1, AnoncredCredentialProofsV1} -import org.hyperledger.identus.shared.models.{WalletAccessContext, WalletId} +import org.hyperledger.identus.shared.models.* import zio.{ZIO, ZLayer} import zio.test.* import zio.test.Assertion.* @@ -317,12 +317,15 @@ object PresentationRepositorySpecSuite { test("updateFail (fail one retry) updates record") { val aRecord = presentationRecord - val failReason = Some("Just to test") + val failReason = Some(FailureInfo("PresentationRepositorySpecSuite", StatusCode(999), "Just to test")) for { repo <- ZIO.service[PresentationRepository] tmp <- repo.createPresentationRecord(aRecord) record0 <- repo.findPresentationRecord(aRecord.id) - _ <- repo.updateAfterFail(aRecord.id, Some("Just to test")) // TEST + _ <- repo.updateAfterFail( + aRecord.id, + Some(FailureInfo("PresentationRepositorySpecSuite", StatusCode(999), "Just to test")) + ) // TEST updatedRecord1 <- repo.findPresentationRecord(aRecord.id) _ <- repo.updatePresentationRecordProtocolState( aRecord.id, @@ -348,12 +351,30 @@ object PresentationRepositorySpecSuite { repo <- ZIO.service[PresentationRepository] _ <- 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")) + _ <- repo.updateAfterFail( + aRecord.id, + Some(FailureInfo("PresentationRepositorySpecSuite", StatusCode(999), "1 - Just to test")) + ) + _ <- repo.updateAfterFail( + aRecord.id, + Some(FailureInfo("PresentationRepositorySpecSuite", StatusCode(999), "2 - Just to test")) + ) + _ <- repo.updateAfterFail( + aRecord.id, + Some(FailureInfo("PresentationRepositorySpecSuite", StatusCode(999), "3 - Just to test")) + ) + _ <- repo.updateAfterFail( + aRecord.id, + Some(FailureInfo("PresentationRepositorySpecSuite", StatusCode(999), "4 - Just to test")) + ) + _ <- repo.updateAfterFail( + aRecord.id, + Some(FailureInfo("PresentationRepositorySpecSuite", StatusCode(999), "5 - Just to test")) + ) + _ <- repo.updateAfterFail( + aRecord.id, + Some(FailureInfo("PresentationRepositorySpecSuite", StatusCode(999), "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.findPresentationRecord(aRecord.id) @@ -362,8 +383,11 @@ object PresentationRepositorySpecSuite { assertTrue(record0.get.metaRetries == maxRetries) && assertTrue(updatedRecord1.get.metaRetries == 0) && // assume the max retries is 5 assertTrue(updatedRecord1.get.metaNextRetry.isEmpty) && - assertTrue(updatedRecord1.get.metaLastFailure.contains("6 - Just to test")) - + assertTrue( + updatedRecord1.get.metaLastFailure.contains( + FailureInfo("PresentationRepositorySpecSuite", StatusCode(999), "6 - Just to test") + ) + ) } } ).provideSomeLayer(ZLayer.succeed(WalletAccessContext(WalletId.random))) @@ -413,7 +437,13 @@ object PresentationRepositorySpecSuite { record1 = presentationRecord record2 = presentationRecord _ <- repo.createPresentationRecord(record1).provide(wallet1) - exit <- repo.updateAfterFail(record2.id, Some("fail reason")).provide(wallet2).exit + exit <- repo + .updateAfterFail( + record2.id, + Some(FailureInfo("PresentationRepositorySpecSuite", StatusCode(999), "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") { diff --git a/pollux/sql-doobie/src/main/resources/sql/pollux/V23__clear_content_of_meta_last_failure.sql b/pollux/sql-doobie/src/main/resources/sql/pollux/V23__clear_content_of_meta_last_failure.sql new file mode 100644 index 0000000000..403c9d44fe --- /dev/null +++ b/pollux/sql-doobie/src/main/resources/sql/pollux/V23__clear_content_of_meta_last_failure.sql @@ -0,0 +1,3 @@ +-- Clear content of meta_last_failure +UPDATE public.issue_credential_records SET meta_last_failure=NULL; +UPDATE public.presentation_records SET meta_last_failure=NULL; \ No newline at end of file diff --git a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialRepository.scala b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialRepository.scala index 3d079f4865..2cab203276 100644 --- a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialRepository.scala +++ b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialRepository.scala @@ -15,7 +15,7 @@ import org.hyperledger.identus.pollux.core.model.* import org.hyperledger.identus.pollux.core.repository.CredentialRepository import org.hyperledger.identus.shared.db.ContextAwareTask import org.hyperledger.identus.shared.db.Implicits.* -import org.hyperledger.identus.shared.models.{KeyId, WalletAccessContext} +import org.hyperledger.identus.shared.models.* import zio.* import zio.interop.catz.* import zio.json.* @@ -55,6 +55,10 @@ class JdbcCredentialRepository(xa: Transactor[ContextAwareTask], xb: Transactor[ given keyIdGet: Get[KeyId] = Get[String].map(KeyId(_)) given keyIdPut: Put[KeyId] = Put[String].contramap(_.value) + + given failureGet: Get[Failure] = Get[String].temap(_.fromJson[FailureInfo]) + given failurePut: Put[Failure] = Put[String].contramap(_.asFailureInfo.toJson) + override def create(record: IssueCredentialRecord): URIO[WalletAccessContext, Unit] = { val cxnIO = sql""" | INSERT INTO public.issue_credential_records( @@ -546,7 +550,7 @@ class JdbcCredentialRepository(xa: Transactor[ContextAwareTask], xb: Transactor[ def updateAfterFail( recordId: DidCommID, - failReason: Option[String] + failReason: Option[Failure] ): URIO[WalletAccessContext, Unit] = { val cxnIO = sql""" | UPDATE public.issue_credential_records 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 6123d81785..5d98bd1f23 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 @@ -16,7 +16,7 @@ import org.hyperledger.identus.pollux.core.model.* 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 +import org.hyperledger.identus.shared.models.* import zio.* import zio.interop.catz.* import zio.json.* @@ -161,6 +161,9 @@ class JdbcPresentationRepository( Get[String].map(decode[ProposePresentation](_).getOrElse(???)) given proposePresentationPut: Put[ProposePresentation] = Put[String].contramap(_.asJson.toString) + given failureGet: Get[Failure] = Get[String].temap(_.fromJson[FailureInfo]) + given failurePut: Put[Failure] = Put[String].contramap(_.asFailureInfo.toJson) + override def createPresentationRecord(record: PresentationRecord): URIO[WalletAccessContext, Unit] = { val cxnIO = sql""" | INSERT INTO public.presentation_records( @@ -488,7 +491,7 @@ class JdbcPresentationRepository( def updateAfterFail( recordId: DidCommID, - failReason: Option[String] + failReason: Option[Failure] ): URIO[WalletAccessContext, Unit] = { val cxnIO = sql""" | UPDATE public.presentation_records diff --git a/shared/core/src/main/scala/org/hyperledger/identus/shared/models/Failure.scala b/shared/core/src/main/scala/org/hyperledger/identus/shared/models/Failure.scala index 3a895223f3..7a7c800ac9 100644 --- a/shared/core/src/main/scala/org/hyperledger/identus/shared/models/Failure.scala +++ b/shared/core/src/main/scala/org/hyperledger/identus/shared/models/Failure.scala @@ -1,11 +1,16 @@ package org.hyperledger.identus.shared.models import zio.{URIO, ZIO} +import zio.json._ trait Failure { - val namespace: String - val statusCode: StatusCode - val userFacingMessage: String + def namespace: String + def statusCode: StatusCode + def userFacingMessage: String + + def toUnmanagedFailureException = UnmanagedFailureException(this) + + def asFailureInfo = FailureInfo(namespace, statusCode, userFacingMessage) } object Failure { @@ -16,11 +21,17 @@ object Failure { } } +final case class FailureInfo(namespace: String, statusCode: StatusCode, userFacingMessage: String) extends Failure +object FailureInfo { + given decoder: JsonDecoder[FailureInfo] = DeriveJsonDecoder.gen[FailureInfo] + given encoder: JsonEncoder[FailureInfo] = DeriveJsonEncoder.gen[FailureInfo] +} + case class UnmanagedFailureException(val failure: Failure) extends Throwable { override def getMessage: String = failure.toString } -sealed class StatusCode(val code: Int) +sealed case class StatusCode(val code: Int) object StatusCode { val BadRequest: StatusCode = StatusCode(400) @@ -30,5 +41,9 @@ object StatusCode { val UnprocessableContent: StatusCode = StatusCode(422) val InternalServerError: StatusCode = StatusCode(500) + val UnexpectedNotImplemented: StatusCode = StatusCode(501) val BadGateway: StatusCode = StatusCode(502) + + given decoder: JsonDecoder[StatusCode] = JsonDecoder.int.map(e => StatusCode(e)) + given encoder: JsonEncoder[StatusCode] = JsonEncoder.int.contramap((e: StatusCode) => e.code) }