• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

input-output-hk / atala-prism-building-blocks / 5254818031

13 Jun 2023 11:23AM UTC coverage: 10.38% (-16.8%) from 27.196%
5254818031

Pull #546

patlo-iog
chore: pr cleanup
Pull Request #546: feat(prism-agent): align DID document service handling with the spec

214 of 214 new or added lines in 10 files covered. (100.0%)

2879 of 27735 relevant lines covered (10.38%)

0.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

27.45
/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/service/DIDService.scala
1
package io.iohk.atala.castor.core.service
2

3
import io.iohk.atala.castor.core.model.did.{
4
  CanonicalPrismDID,
5
  DIDData,
6
  DIDMetadata,
7
  InternalPublicKey,
8
  LongFormPrismDID,
9
  PrismDID,
10
  PublicKey,
11
  ScheduleDIDOperationOutcome,
12
  ScheduledDIDOperationDetail,
13
  SignedPrismDIDOperation
14
}
15
import zio.*
16
import io.iohk.atala.castor.core.model.ProtoModelHelper
17
import io.iohk.atala.castor.core.model.error.{DIDOperationError, DIDResolutionError}
18
import io.iohk.atala.castor.core.util.DIDOperationValidator
19
import io.iohk.atala.shared.models.HexString
20
import io.iohk.atala.prism.protos.{node_api, node_models}
21
import io.iohk.atala.prism.protos.node_api.NodeServiceGrpc.NodeService
22
import io.iohk.atala.prism.protos.node_models.OperationOutput.OperationMaybe
23

24
import scala.collection.immutable.ArraySeq
25
import io.iohk.atala.castor.core.model.error.OperationValidationError
26

27
trait DIDService {
28
  def scheduleOperation(operation: SignedPrismDIDOperation): IO[DIDOperationError, ScheduleDIDOperationOutcome]
29
  def getScheduledDIDOperationDetail(
30
      operationId: Array[Byte]
31
  ): IO[DIDOperationError, Option[ScheduledDIDOperationDetail]]
32
  def resolveDID(did: PrismDID): IO[DIDResolutionError, Option[(DIDMetadata, DIDData)]]
33
}
34

35
object DIDServiceImpl {
36
  val layer: URLayer[NodeService & DIDOperationValidator, DIDService] =
1✔
37
    ZLayer.fromFunction(DIDServiceImpl(_, _))
38
}
39

40
private class DIDServiceImpl(didOpValidator: DIDOperationValidator, nodeClient: NodeService)
41
    extends DIDService,
42
      ProtoModelHelper {
43

×
44
  override def scheduleOperation(
45
      signedOperation: SignedPrismDIDOperation
46
  ): IO[DIDOperationError, ScheduleDIDOperationOutcome] = {
×
47
    val operationRequest = node_api.ScheduleOperationsRequest(
×
48
      signedOperations = Seq(signedOperation.toProto)
49
    )
×
50
    for {
×
51
      _ <- ZIO
×
52
        .fromEither(didOpValidator.validate(signedOperation.operation))
53
        .mapError(DIDOperationError.ValidationError.apply)
×
54
      operationOutput <- ZIO
×
55
        .fromFuture(_ => nodeClient.scheduleOperations(operationRequest))
×
56
        .mapBoth(DIDOperationError.DLTProxyError.apply, _.outputs.toList)
57
        .map {
×
58
          case output :: Nil => Right(output)
×
59
          case _ => Left(DIDOperationError.UnexpectedDLTResult("operation result is expected to have exactly 1 output"))
60
        }
61
        .absolve
×
62
      operationId <- ZIO.fromEither {
63
        operationOutput.operationMaybe match {
×
64
          case OperationMaybe.OperationId(id) => Right(id.toByteArray)
×
65
          case OperationMaybe.Empty =>
×
66
            Left(DIDOperationError.UnexpectedDLTResult("operation result does not contain operation detail"))
×
67
          case OperationMaybe.Error(e) =>
×
68
            Left(DIDOperationError.UnexpectedDLTResult(s"operation result was not successful: $e"))
69
        }
70
      }
71
    } yield ScheduleDIDOperationOutcome(
×
72
      did = signedOperation.operation.did,
73
      operation = signedOperation.operation,
×
74
      operationId = ArraySeq.from(operationId)
75
    )
76
  }
77

×
78
  override def getScheduledDIDOperationDetail(
79
      operationId: Array[Byte]
80
  ): IO[DIDOperationError, Option[ScheduledDIDOperationDetail]] = {
×
81
    for {
×
82
      result <- ZIO
×
83
        .fromFuture(_ => nodeClient.getOperationInfo(node_api.GetOperationInfoRequest(operationId.toProto)))
84
        .mapError(DIDOperationError.DLTProxyError.apply)
×
85
      detail <- ZIO
×
86
        .fromEither(result.toDomain)
87
        .mapError(DIDOperationError.UnexpectedDLTResult.apply)
88
    } yield detail
89
  }
90

1✔
91
  override def resolveDID(did: PrismDID): IO[DIDResolutionError, Option[(DIDMetadata, DIDData)]] = {
1✔
92
    val canonicalDID = did.asCanonical
1✔
93
    val request = node_api.GetDidDocumentRequest(did = canonicalDID.toString)
1✔
94
    for {
95
      unpublishedDidData <- did match {
×
96
        case _: CanonicalPrismDID => ZIO.none
×
97
        case d: LongFormPrismDID  => extractUnpublishedDIDData(d).asSome
98
      }
1✔
99
      result <- ZIO
×
100
        .fromFuture(_ => nodeClient.getDidDocument(request))
101
        .mapError(DIDResolutionError.DLTProxyError.apply)
1✔
102
      publishedDidData <- ZIO
103
        .fromOption(result.document)
104
        .foldZIO(
105
          _ => ZIO.none,
106
          didDataProto =>
×
107
            didDataProto.filterRevokedKeysAndServices
×
108
              .flatMap(didData => ZIO.fromEither(didData.toDomain))
109
              .mapError(DIDResolutionError.UnexpectedDLTResult.apply)
110
              .map { didData =>
111
                val metadata = DIDMetadata(
×
112
                  lastOperationHash = ArraySeq.from(result.lastUpdateOperation.toByteArray),
×
113
                  deactivated = didData.internalKeys.isEmpty && didData.publicKeys.isEmpty
114
                )
×
115
                metadata -> didData
116
              }
117
              .asSome
118
        )
1✔
119
    } yield publishedDidData.orElse(unpublishedDidData)
120
  }
121

1✔
122
  private def extractUnpublishedDIDData(did: LongFormPrismDID): IO[DIDResolutionError, (DIDMetadata, DIDData)] = {
×
123
    ZIO
×
124
      .fromEither(did.createOperation)
125
      .mapError(e => DIDResolutionError.ValidationError(OperationValidationError.InvalidArgument(e)))
126
      .flatMap { op =>
127
        // unpublished CreateOperation (if exists) must be validated before the resolution
×
128
        ZIO
×
129
          .fromEither(didOpValidator.validate(op))
130
          .mapError(DIDResolutionError.ValidationError.apply)
131
          .as(op)
132
      }
133
      .map { op =>
134
        val metadata =
135
          DIDMetadata(
1✔
136
            lastOperationHash = ArraySeq.from(did.stateHash.toByteArray),
137
            deactivated = false // unpublished DID cannot be deactivated
138
          )
139
        val didData = DIDData(
1✔
140
          id = did.asCanonical,
1✔
141
          publicKeys = op.publicKeys.collect { case pk: PublicKey => pk },
142
          services = op.services,
1✔
143
          internalKeys = op.publicKeys.collect { case pk: InternalPublicKey => pk },
144
          context = op.context
145
        )
1✔
146
        metadata -> didData
147
      }
148
  }
149

150
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc