Skip to content

Commit

Permalink
feat(prism-agent): implement get DIDs endpoint (#198)
Browse files Browse the repository at this point in the history
* feat(prism-agent): add listManagedDID endpoint

* feat(prism-agent): refresh state from DLT before returning result

* feat(prism-agent): proper error for list did endpoint
  • Loading branch information
patlo-iog committed Dec 6, 2022
1 parent 811c034 commit d5e08ab
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 14 deletions.
24 changes: 23 additions & 1 deletion prism-agent/service/api/http/castor/schemas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ components:
properties:
did:
type: string
example: did:prism:unpublished:7a3a5cd1beab57a65c18c584848029c513ca27c8edfb69ceb2c7aef3d659bf44?instance=https://offchain-storage.com"
example: did:prism:7a3a5cd1beab57a65c18c584848029c513ca27c8edfb69ceb2c7aef3d659bf44?instance=https://offchain-storage.com"
updatePublicKey:
type: string
example: "EiBkRSeixqX-PhOij6PIpuGfPld5Nif5MxcrgtGCw-t6LA"
Expand Down Expand Up @@ -232,6 +232,28 @@ components:
description: A long-form DID for the created DID
example: did:prism:1:abc123:abc123

ListManagedDIDResponse:
type: array
items:
type: object
required:
- did
- status
properties:
did:
type: string
example: did:prism:abc
longFormDid:
type: string
description: A long-form DID. Mandatory when status is not PUBLISHED and optional when status is PUBLISHED
example: did:prism:abc:123
status:
type: string
enum:
- CREATED
- PUBLICATION_PENDING
- PUBLISHED

Delta:
type: object
required:
Expand Down
31 changes: 25 additions & 6 deletions prism-agent/service/api/http/prism-agent-openapi-spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,25 @@ paths:
$ref: "./castor/schemas.yaml#/components/schemas/ErrorResponse"

/did-registrar/dids:
get:
tags: [ "DID Registrar" ]
operationId: listManagedDid
summary: List all DIDs managed by PrismAgent.
description: List all DIDs managed by PrismAgent.
responses:
"200":
description: List managed DIDs
content:
application/json:
schema:
$ref: "./castor/schemas.yaml#/components/schemas/ListManagedDIDResponse"
"422":
description: The DID creation failed.
content:
application/json:
schema:
$ref: "./castor/schemas.yaml#/components/schemas/ErrorResponse"

post:
tags: [ "DID Registrar" ]
operationId: createManagedDid
Expand Down Expand Up @@ -745,7 +764,7 @@ paths:
application/json:
schema:
$ref: "./castor/schemas.yaml#/components/schemas/ErrorResponse"

/issue-credentials/records:
get:
tags: [ "Issue Credentials Protocol" ]
Expand Down Expand Up @@ -785,7 +804,7 @@ paths:
application/json:
schema:
$ref: "./pollux/schemas.yaml#/components/schemas/ErrorResponse"

/issue-credentials/records/{recordId}/accept-offer:
post:
tags: [ "Issue Credentials Protocol" ]
Expand Down Expand Up @@ -978,9 +997,9 @@ paths:
operationId: createConnection
summary: Creates new connection and returns an invitation.
description: |-
Returns new invitation object and creates new connection state record in `pending` state.
Content of invitation depends on DIDComm protocol used, here is an example of how it would look like for `AIP 1.0 connection/v1` protocol.
Once connection invitation is accepted, Agent should filter all additional attempts to accept it.
Returns new invitation object and creates new connection state record in `pending` state.
Content of invitation depends on DIDComm protocol used, here is an example of how it would look like for `AIP 1.0 connection/v1` protocol.
Once connection invitation is accepted, Agent should filter all additional attempts to accept it.
We consider mult-party connections as out of scope for now.
requestBody:
required: true
Expand Down Expand Up @@ -1079,4 +1098,4 @@ paths:
content:
application/json:
schema:
$ref: "./connect/schemas.yaml#/components/schemas/ErrorResponse"
$ref: "./connect/schemas.yaml#/components/schemas/ErrorResponse"
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ import io.iohk.atala.agent.openapi.model.{
CreateManagedDIDResponse,
CreateManagedDidRequest,
DIDOperationResponse,
ErrorResponse
ErrorResponse,
ListManagedDIDResponseInner
}
import spray.json.RootJsonFormat
import zio.*

object DIDRegistrarApiMarshallerImpl extends JsonSupport {

val layer: ULayer[DIDRegistrarApiMarshaller] = ZLayer.succeed {
new DIDRegistrarApiMarshaller:
new DIDRegistrarApiMarshaller {
override implicit def fromEntityUnmarshallerCreateManagedDidRequest
: FromEntityUnmarshaller[CreateManagedDidRequest] = summon[RootJsonFormat[CreateManagedDidRequest]]

Expand All @@ -25,8 +26,13 @@ object DIDRegistrarApiMarshallerImpl extends JsonSupport {
override implicit def toEntityMarshallerCreateManagedDIDResponse: ToEntityMarshaller[CreateManagedDIDResponse] =
summon[RootJsonFormat[CreateManagedDIDResponse]]

override implicit def toEntityMarshallerListManagedDIDResponseInnerarray
: ToEntityMarshaller[Seq[ListManagedDIDResponseInner]] =
summon[RootJsonFormat[Seq[ListManagedDIDResponseInner]]]

override implicit def toEntityMarshallerErrorResponse: ToEntityMarshaller[ErrorResponse] =
summon[RootJsonFormat[ErrorResponse]]
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol {
given RootJsonFormat[DidOperationType] = jsonFormat0(DidOperationType.apply)
given RootJsonFormat[DIDResponse] = jsonFormat2(DIDResponse.apply)
given RootJsonFormat[ErrorResponse] = jsonFormat5(ErrorResponse.apply)
given RootJsonFormat[ListManagedDIDResponseInner] = jsonFormat3(ListManagedDIDResponseInner.apply)
given RootJsonFormat[PublicKey] = jsonFormat5(PublicKey.apply)
given RootJsonFormat[PublicKeyJwk] = jsonFormat5(PublicKeyJwk.apply)
given RootJsonFormat[RecoverDIDRequest] = jsonFormat5(RecoverDIDRequest.apply)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import io.iohk.atala.agent.openapi.model.{
DidOperationSubmission,
IssueCredentialRecord,
IssueCredentialRecordCollection,
ListManagedDIDResponseInner,
PublicKey,
PublicKeyJwk,
Service,
Expand All @@ -35,6 +36,8 @@ import io.iohk.atala.mercury.model.AttachmentDescriptor
import io.iohk.atala.mercury.model.Base64
import zio.ZIO
import io.iohk.atala.agent.server.http.model.HttpServiceError.InvalidPayload
import io.iohk.atala.agent.walletapi.model.ManagedDIDState
import io.iohk.atala.castor.core.model.did.{LongFormPrismDID, PrismDID}

import java.util.UUID
import io.iohk.atala.connect.core.model.ConnectionRecord.Role
Expand Down Expand Up @@ -202,4 +205,20 @@ trait OASDomainModelHelper {
)
}

extension (didDetail: walletDomain.ManagedDIDDetail) {
def toOAS: ListManagedDIDResponseInner = {
val (longFormDID, status) = didDetail.state match {
case ManagedDIDState.Created(operation) => Some(PrismDID.buildLongFormFromOperation(operation)) -> "CREATED"
case ManagedDIDState.PublicationPending(operation, _) =>
Some(PrismDID.buildLongFormFromOperation(operation)) -> "PUBLICATION_PENDING"
case ManagedDIDState.Published(_, _) => None -> "PUBLISHED"
}
ListManagedDIDResponseInner(
did = didDetail.did.toString,
longFormDid = longFormDID.map(_.toString),
status = status
)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ package io.iohk.atala.agent.server.http.model

import akka.http.scaladsl.server.StandardRoute
import io.iohk.atala.agent.openapi.model.ErrorResponse
import io.iohk.atala.agent.walletapi.model.error.CreateManagedDIDError
import io.iohk.atala.agent.walletapi.model.error.PublishManagedDIDError
import io.iohk.atala.agent.walletapi.model.error.{CreateManagedDIDError, ListManagedDIDError, PublishManagedDIDError}
import io.iohk.atala.castor.core.model.did.w3c.DIDResolutionErrorRepr
import io.iohk.atala.castor.core.model.error.DIDOperationError
import io.iohk.atala.castor.core.model.error.DIDResolutionError
Expand Down Expand Up @@ -46,6 +45,18 @@ trait OASErrorModelHelper {
}
}

given ToErrorResponse[ListManagedDIDError] with {
override def toErrorResponse(e: ListManagedDIDError): ErrorResponse = {
ErrorResponse(
`type` = "error-type",
title = "error-title",
status = 500,
detail = Some(e.toString),
instance = "error-instance"
)
}
}

given ToErrorResponse[PublishManagedDIDError] with {
override def toErrorResponse(e: PublishManagedDIDError): ErrorResponse = {
ErrorResponse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,20 @@ class DIDRegistrarApiServiceImpl(service: ManagedDIDService)(using runtime: Runt
}
}

override def listManagedDid()(implicit
toEntityMarshallerListManagedDIDResponseInnerarray: ToEntityMarshaller[Seq[ListManagedDIDResponseInner]],
toEntityMarshallerErrorResponse: ToEntityMarshaller[ErrorResponse]
): Route = {
val result = service.listManagedDID
.map(_.map(_.toOAS))
.mapError(HttpServiceError.DomainError.apply)

onZioSuccess(result.mapError(_.toOAS).either) {
case Left(error) => complete(error.status -> error)
case Right(result) => listManagedDid200(result)
}
}

override def publishManagedDid(didRef: String)(implicit
toEntityMarshallerDIDOperationResponse: ToEntityMarshaller[DIDOperationResponse],
toEntityMarshallerErrorResponse: ToEntityMarshaller[ErrorResponse]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package io.iohk.atala.agent.walletapi.model

import io.iohk.atala.castor.core.model.did.PrismDIDOperation
import io.iohk.atala.castor.core.model.did.{CanonicalPrismDID, PrismDIDOperation}

import scala.collection.immutable.ArraySeq

final case class ManagedDIDDetail(did: CanonicalPrismDID, state: ManagedDIDState)

sealed trait ManagedDIDState

object ManagedDIDState {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.iohk.atala.agent.walletapi.model.error

import io.iohk.atala.castor.core.model.error.DIDOperationError

sealed trait ListManagedDIDError

object ListManagedDIDError {
final case class WalletStorageError(cause: Throwable) extends ListManagedDIDError
final case class OperationError(cause: DIDOperationError) extends ListManagedDIDError
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package io.iohk.atala.agent.walletapi.service

import io.iohk.atala.agent.walletapi.crypto.{ECWrapper, KeyGeneratorWrapper}
import io.iohk.atala.agent.walletapi.model.{DIDPublicKeyTemplate, ECKeyPair, ManagedDIDState, ManagedDIDTemplate}
import io.iohk.atala.agent.walletapi.model.{
DIDPublicKeyTemplate,
ECKeyPair,
ManagedDIDDetail,
ManagedDIDState,
ManagedDIDTemplate
}
import io.iohk.atala.agent.walletapi.model.ECCoordinates.*
import io.iohk.atala.agent.walletapi.model.error.{CreateManagedDIDError, PublishManagedDIDError}
import io.iohk.atala.agent.walletapi.model.error.{CreateManagedDIDError, ListManagedDIDError, PublishManagedDIDError}
import io.iohk.atala.agent.walletapi.service.ManagedDIDService.{CreateDIDSecret, DEFAULT_MASTER_KEY_ID}
import io.iohk.atala.agent.walletapi.storage.{
DIDNonSecretStorage,
Expand Down Expand Up @@ -50,6 +56,28 @@ final class ManagedDIDService private[walletapi] (

private val CURVE = EllipticCurve.SECP256K1

def listManagedDID: IO[ListManagedDIDError, Seq[ManagedDIDDetail]] = nonSecretStorage.listManagedDID
.mapBoth(
ListManagedDIDError.WalletStorageError.apply,
_.toSeq.map { case (did, state) =>
ManagedDIDDetail(did = did.asCanonical, state = state)
}
)
.flatMap { dids =>
ZIO.foreach(dids) { didDetail =>
// state in wallet maybe stale, update it from DLT
syncDIDStateFromDLT(didDetail.state)
.mapError(ListManagedDIDError.OperationError.apply)
.tap(state =>
nonSecretStorage
.setManagedDIDState(didDetail.did, state)
.mapError(ListManagedDIDError.WalletStorageError.apply)
)
.map(didDetail -> _)
}
}
.map(_.map { case (didDetail, newState) => didDetail.copy(state = newState) })

def publishStoredDID(did: CanonicalPrismDID): IO[PublishManagedDIDError, ScheduleDIDOperationOutcome] = {
def syncDLTStateAndPersist =
nonSecretStorage
Expand Down

0 comments on commit d5e08ab

Please sign in to comment.