Skip to content

Commit

Permalink
feat: Integrate ZIO failures and defects ADR in DID Registrar Control…
Browse files Browse the repository at this point in the history
…ler and Mercury (#1180)

Related with the tickets ATL-6831 and parcial for ATL-7257

Signed-off-by: FabioPinheiro <[email protected]>
  • Loading branch information
FabioPinheiro authored Jun 14, 2024
1 parent 1ed1d6c commit d8e2120
Show file tree
Hide file tree
Showing 16 changed files with 218 additions and 166 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,7 @@ private class DIDServiceImpl(didOpValidator: DIDOperationValidator, nodeClient:
override def scheduleOperation(
signedOperation: SignedPrismDIDOperation
): IO[DIDOperationError, ScheduleDIDOperationOutcome] = {
val operationRequest = node_api.ScheduleOperationsRequest(
signedOperations = Seq(signedOperation.toProto)
)
val operationRequest = node_api.ScheduleOperationsRequest(signedOperations = Seq(signedOperation.toProto))
for {
_ <- ZIO
.fromEither(didOpValidator.validate(signedOperation.operation))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,15 @@ private object CreateOperationValidator extends BaseOperationValidator {
case InternalPublicKey(id, purpose, data) => (id, purpose, data)
}

private def extractServiceIds(operation: PrismDIDOperation.Create): Seq[String] = operation.services.map(_.id)
private def extractServiceIds(operation: PrismDIDOperation.Create): Seq[String] =
operation.services.map(_.id)

private def extractServiceEndpoint(operation: PrismDIDOperation.Create): Seq[(String, ServiceEndpoint)] = {
private def extractServiceEndpoint(operation: PrismDIDOperation.Create): Seq[(String, ServiceEndpoint)] =
operation.services.map { s => (s.id, s.serviceEndpoint) }
}

private def extractServiceType(operation: PrismDIDOperation.Create): Seq[(String, ServiceType)] = {
private def extractServiceType(operation: PrismDIDOperation.Create): Seq[(String, ServiceType)] =
operation.services.map { s => (s.id, s.`type`) }
}

}

private object UpdateOperationValidator extends BaseOperationValidator {
Expand Down Expand Up @@ -346,16 +346,13 @@ private trait BaseOperationValidator {
protected def validatePreviousOperationHash[T <: PrismDIDOperation](
operation: T,
previousOperationHashExtractor: T => ArraySeq[Byte]
): Either[OperationValidationError, Unit] = {
val previousOperationHash = previousOperationHashExtractor(operation)
if (previousOperationHash.length == 32) Right(())
): Either[OperationValidationError, Unit] =
if (previousOperationHashExtractor(operation).length == 32) Right(())
else Left(OperationValidationError.InvalidArgument(s"previousOperationHash must have a size of 32 bytes"))
}

/** @return true if a given uri is normalized */
protected def isUriNormalized(uri: String): Boolean = {
protected def isUriNormalized(uri: String): Boolean =
UriUtils.normalizeUri(uri).contains(uri)
}

protected def validateMasterKeyIsSecp256k1[T <: PrismDIDOperation](
operation: T,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,25 @@ import zio.*
import scala.language.implicitConversions

trait DIDRegistrarController {
def listManagedDid(paginationInput: PaginationInput)(implicit
def listManagedDid(paginationInput: PaginationInput)(using
rc: RequestContext
): ZIO[WalletAccessContext, ErrorResponse, ManagedDIDPage]

def createManagedDid(request: CreateManagedDidRequest)(implicit
def createManagedDid(request: CreateManagedDidRequest)(using
rc: RequestContext
): ZIO[WalletAccessContext, ErrorResponse, CreateManagedDIDResponse]

def getManagedDid(did: String)(implicit rc: RequestContext): ZIO[WalletAccessContext, ErrorResponse, ManagedDID]
def getManagedDid(did: String)(using rc: RequestContext): ZIO[WalletAccessContext, ErrorResponse, ManagedDID]

def publishManagedDid(did: String)(implicit
def publishManagedDid(did: String)(using
rc: RequestContext
): ZIO[WalletAccessContext, ErrorResponse, DIDOperationResponse]

def updateManagedDid(did: String, updateRequest: UpdateManagedDIDRequest)(implicit
def updateManagedDid(did: String, updateRequest: UpdateManagedDIDRequest)(using
rc: RequestContext
): ZIO[WalletAccessContext, ErrorResponse, DIDOperationResponse]

def deactivateManagedDid(did: String)(implicit
def deactivateManagedDid(did: String)(using
rc: RequestContext
): ZIO[WalletAccessContext, ErrorResponse, DIDOperationResponse]
}
Expand All @@ -55,14 +55,14 @@ object DIDRegistrarController {
case GetManagedDIDError.OperationError(e) =>
ErrorResponse.internalServerError(detail = Some(e.toString))
case GetManagedDIDError.WalletStorageError(e) =>
ErrorResponse.internalServerError(detail = Some(e.toString))
ErrorResponse.internalServerError(detail = Some(e.getMessage))
}

given Conversion[CreateManagedDIDError, ErrorResponse] = {
case CreateManagedDIDError.InvalidArgument(msg) =>
ErrorResponse.unprocessableEntity(detail = Some(msg))
case CreateManagedDIDError.WalletStorageError(e) =>
ErrorResponse.internalServerError(detail = Some(e.toString))
ErrorResponse.internalServerError(detail = Some(e.getMessage))
case CreateManagedDIDError.InvalidOperation(e) =>
ErrorResponse.unprocessableEntity(detail = Some(e.toString))
}
Expand All @@ -71,7 +71,7 @@ object DIDRegistrarController {
case PublishManagedDIDError.DIDNotFound(did) =>
ErrorResponse.notFound(detail = Some(s"DID not found: $did"))
case PublishManagedDIDError.WalletStorageError(e) =>
ErrorResponse.internalServerError(detail = Some(e.toString))
ErrorResponse.internalServerError(detail = Some(e.getMessage))
case PublishManagedDIDError.OperationError(e) =>
ErrorResponse.internalServerError(detail = Some(e.toString))
case PublishManagedDIDError.CryptographyError(e) =>
Expand Down Expand Up @@ -99,7 +99,7 @@ class DIDRegistrarControllerImpl(service: ManagedDIDService) extends DIDRegistra

override def listManagedDid(
paginationInput: PaginationInput
)(implicit rc: RequestContext): ZIO[WalletAccessContext, ErrorResponse, ManagedDIDPage] = {
)(using rc: RequestContext): ZIO[WalletAccessContext, ErrorResponse, ManagedDIDPage] = {
val uri = rc.request.uri
val pagination = paginationInput.toPagination
for {
Expand All @@ -117,7 +117,7 @@ class DIDRegistrarControllerImpl(service: ManagedDIDService) extends DIDRegistra
)
}

override def createManagedDid(createManagedDidRequest: CreateManagedDidRequest)(implicit
override def createManagedDid(createManagedDidRequest: CreateManagedDidRequest)(using
rc: RequestContext
): ZIO[WalletAccessContext, ErrorResponse, CreateManagedDIDResponse] = {
for {
Expand All @@ -132,7 +132,7 @@ class DIDRegistrarControllerImpl(service: ManagedDIDService) extends DIDRegistra

override def getManagedDid(
did: String
)(implicit rc: RequestContext): ZIO[WalletAccessContext, ErrorResponse, ManagedDID] = {
)(using rc: RequestContext): ZIO[WalletAccessContext, ErrorResponse, ManagedDID] = {
for {
prismDID <- extractPrismDID(did)
didDetail <- service
Expand All @@ -145,7 +145,7 @@ class DIDRegistrarControllerImpl(service: ManagedDIDService) extends DIDRegistra

override def publishManagedDid(
did: String
)(implicit rc: RequestContext): ZIO[WalletAccessContext, ErrorResponse, DIDOperationResponse] = {
)(using rc: RequestContext): ZIO[WalletAccessContext, ErrorResponse, DIDOperationResponse] = {
for {
prismDID <- extractPrismDID(did)
outcome <- service
Expand All @@ -154,7 +154,7 @@ class DIDRegistrarControllerImpl(service: ManagedDIDService) extends DIDRegistra
} yield outcome
}

override def updateManagedDid(did: String, updateRequest: UpdateManagedDIDRequest)(implicit
override def updateManagedDid(did: String, updateRequest: UpdateManagedDIDRequest)(using
rc: RequestContext
): ZIO[WalletAccessContext, ErrorResponse, DIDOperationResponse] = {
for {
Expand All @@ -170,7 +170,7 @@ class DIDRegistrarControllerImpl(service: ManagedDIDService) extends DIDRegistra

override def deactivateManagedDid(
did: String
)(implicit rc: RequestContext): ZIO[WalletAccessContext, ErrorResponse, DIDOperationResponse] = {
)(using rc: RequestContext): ZIO[WalletAccessContext, ErrorResponse, DIDOperationResponse] = {
for {
prismDID <- extractPrismDID(did)
outcome <- service
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class DIDRegistrarServerEndpoints(
.serverLogic { wac =>
{ case (rc, paginationInput) =>
didRegistrarController
.listManagedDid(paginationInput)(rc)
.listManagedDid(paginationInput)(using rc)
.provideSomeLayer(ZLayer.succeed(wac))
.logTrace(rc)
}
Expand All @@ -31,7 +31,7 @@ class DIDRegistrarServerEndpoints(
.serverLogic { wac =>
{ case (rc, createManagedDidRequest) =>
didRegistrarController
.createManagedDid(createManagedDidRequest)(rc)
.createManagedDid(createManagedDidRequest)(using rc)
.provideSomeLayer(ZLayer.succeed(wac))
.logTrace(rc)
}
Expand All @@ -43,7 +43,7 @@ class DIDRegistrarServerEndpoints(
.serverLogic { wac =>
{ case (rc, did) =>
didRegistrarController
.getManagedDid(did)(rc)
.getManagedDid(did)(using rc)
.provideSomeLayer(ZLayer.succeed(wac))
.logTrace(rc)
}
Expand All @@ -55,7 +55,7 @@ class DIDRegistrarServerEndpoints(
.serverLogic { wac =>
{ case (rc, did) =>
didRegistrarController
.publishManagedDid(did)(rc)
.publishManagedDid(did)(using rc)
.provideSomeLayer(ZLayer.succeed(wac))
.logTrace(rc)
}
Expand All @@ -67,7 +67,7 @@ class DIDRegistrarServerEndpoints(
.serverLogic { wac =>
{ case (rc, did, updateRequest) =>
didRegistrarController
.updateManagedDid(did, updateRequest)(rc)
.updateManagedDid(did, updateRequest)(using rc)
.provideSomeLayer(ZLayer.succeed(wac))
.logTrace(rc)
}
Expand All @@ -79,7 +79,7 @@ class DIDRegistrarServerEndpoints(
.serverLogic { wac =>
{ case (rc, did) =>
didRegistrarController
.deactivateManagedDid(did)(rc)
.deactivateManagedDid(did)(using rc)
.provideSomeLayer(ZLayer.succeed(wac))
.logTrace(rc)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ import org.hyperledger.identus.didcomm.controller.http.DIDCommMessage
import zio.IO

trait DIDCommController {
def handleDIDCommMessage(msg: DIDCommMessage)(implicit rc: RequestContext): IO[ErrorResponse, Unit]
def handleDIDCommMessage(msg: DIDCommMessage)(using rc: RequestContext): IO[ErrorResponse, Unit]
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class DIDCommControllerImpl(
appConfig: AppConfig
) extends DIDCommController {

override def handleDIDCommMessage(msg: DIDCommMessage)(implicit rc: RequestContext): IO[ErrorResponse, Unit] = {
override def handleDIDCommMessage(msg: DIDCommMessage)(using rc: RequestContext): IO[ErrorResponse, Unit] = {
for {
_ <- validateContentType(rc.request.contentType)
_ <- handleMessage(msg)
Expand All @@ -53,10 +53,8 @@ class DIDCommControllerImpl(
msgAndContext <- unpackMessage(msg)
_ <- processMessage(msgAndContext._1)
.catchAll {
case t: Throwable => ZIO.die(t) // Convert any 'Throwable' failure to a defect
case f: Failure => ZIO.fail(f)
case _: DIDCommMessageParsingError => ZIO.fail(UnexpectedError(StatusCode.BadRequest))
case _: CredentialServiceError => ZIO.fail(UnexpectedError(StatusCode.UnprocessableContent))
case _: PresentationError => ZIO.fail(UnexpectedError(StatusCode.UnprocessableContent))
}
.provideSomeLayer(ZLayer.succeed(msgAndContext._2))
Expand Down Expand Up @@ -126,19 +124,25 @@ class DIDCommControllerImpl(
: PartialFunction[Message, ZIO[WalletAccessContext, CredentialServiceError, Unit]] = {
case msg if msg.piuri == OfferCredential.`type` =>
for {
offerFromIssuer <- ZIO.succeed(OfferCredential.readFromMessage(msg))
offerFromIssuer <- ZIO
.fromEither(OfferCredential.readFromMessage(msg))
.mapError(CredentialServiceError.InvalidCredentialOffer(_))
_ <- ZIO.logInfo("As an Holder in issue-credential got OfferCredential: " + offerFromIssuer)
_ <- credentialService.receiveCredentialOffer(offerFromIssuer)
} yield ()
case msg if msg.piuri == RequestCredential.`type` =>
for {
requestCredential <- ZIO.succeed(RequestCredential.readFromMessage(msg))
requestCredential <- ZIO
.fromEither(RequestCredential.readFromMessage(msg))
.mapError(CredentialServiceError.InvalidCredentialRequest(_))
_ <- ZIO.logInfo("As an Issuer in issue-credential got RequestCredential: " + requestCredential)
_ <- credentialService.receiveCredentialRequest(requestCredential)
} yield ()
case msg if msg.piuri == IssueCredential.`type` =>
for {
issueCredential <- ZIO.succeed(IssueCredential.readFromMessage(msg))
issueCredential <- ZIO
.fromEither(IssueCredential.readFromMessage(msg))
.mapError(CredentialServiceError.InvalidCredentialIssue(_))
_ <- ZIO.logInfo("As an Holder in issue-credential got IssueCredential: " + issueCredential)
_ <- credentialService.receiveCredentialIssue(issueCredential)
} yield ()
Expand Down Expand Up @@ -168,10 +172,12 @@ class DIDCommControllerImpl(
} yield ()
}

private val revocationNotification: PartialFunction[Message, ZIO[Any, Throwable, Unit]] = {
private val revocationNotification: PartialFunction[Message, ZIO[Any, DIDCommMessageParsingError, Unit]] = {
case msg if msg.piuri == RevocationNotification.`type` =>
for {
revocationNotification <- ZIO.attempt(RevocationNotification.readFromMessage(msg))
revocationNotification <- ZIO
.fromEither(RevocationNotification.readFromMessage(msg))
.mapError(DIDCommMessageParsingError(_))
_ <- ZIO.logInfo("Got RevocationNotification: " + revocationNotification)
} yield ()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class DIDCommServerEndpoints(
) {
private val handleDIDCommMessageServerEndpoint: ZServerEndpoint[Any, Any] = DIDCommEndpoints.handleDIDCommMessage
.zServerLogic { case (ctx: RequestContext, msg: DIDCommMessage) =>
didCommController.handleDIDCommMessage(msg)(ctx)
didCommController.handleDIDCommMessage(msg)(using ctx)
}

val all: List[ZServerEndpoint[Any, Any]] = List(handleDIDCommMessageServerEndpoint)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import org.hyperledger.identus.agent.walletapi.model.error.CommonWalletStorageEr
import org.hyperledger.identus.agent.walletapi.model.ManagedDIDDetail
import org.hyperledger.identus.agent.walletapi.storage.{DIDNonSecretStorage, DIDSecretStorage, WalletSecretStorage}
import org.hyperledger.identus.castor.core.model.did.CanonicalPrismDID
import org.hyperledger.identus.castor.core.model.error
import org.hyperledger.identus.castor.core.model.error.DIDOperationError
import org.hyperledger.identus.castor.core.service.DIDService
import org.hyperledger.identus.castor.core.util.DIDOperationValidator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,36 +73,43 @@ object IssueCredential {
given Decoder[Body] = deriveDecoder[Body]
}

def makeIssueCredentialFromRequestCredential(msg: Message): IssueCredential = {
val rc: RequestCredential = RequestCredential.readFromMessage(msg)
def makeIssueCredentialFromRequestCredential(msg: Message): Either[String, IssueCredential] =
RequestCredential.readFromMessage(msg).map { rc =>
IssueCredential(
body = IssueCredential.Body(
goal_code = rc.body.goal_code,
comment = rc.body.comment,
replacement_id = None,
more_available = None,
),
attachments = rc.attachments,
thid = msg.thid.orElse(Some(rc.id)),
from = rc.to,
to = rc.from,
)
}

IssueCredential(
body = IssueCredential.Body(
goal_code = rc.body.goal_code,
comment = rc.body.comment,
replacement_id = None,
more_available = None,
),
attachments = rc.attachments,
thid = msg.thid.orElse(Some(rc.id)),
from = rc.to,
to = rc.from,
)
def readFromMessage(message: Message): Either[String, IssueCredential] = {
message.body.asJson.as[IssueCredential.Body] match
case Left(fail) => Left("Fail to parse IssueCredential's body: " + fail.getMessage)
case Right(body) =>
message.from match
case None => Left("IssueCredential MUST have the sender explicit")
case Some(from) =>
message.to match
case firstTo +: Seq() =>
Right(
IssueCredential(
id = message.id,
`type` = message.piuri,
body = body,
attachments = message.attachments.getOrElse(Seq.empty),
thid = message.thid,
from = from,
to = firstTo
)
)
case tos => Left(s"IssueCredential MUST have only 1 recipient instead has '${tos}'")
}

def readFromMessage(message: Message): IssueCredential = {
val body = message.body.asJson.as[IssueCredential.Body].toOption.get // TODO get
IssueCredential(
id = message.id,
`type` = message.piuri,
body = body,
attachments = message.attachments.getOrElse(Seq.empty),
thid = message.thid,
from = message.from.get, // TODO get
to = {
assert(message.to.length == 1, "The recipient is ambiguous. Need to have only 1 recipient") // TODO return error
message.to.head
},
)
}
}
Loading

0 comments on commit d8e2120

Please sign in to comment.