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

hyperledger / identus-cloud-agent / 10793991050

10 Sep 2024 01:56PM CUT coverage: 48.504% (-4.5%) from 52.962%
10793991050

push

web-flow
build: sbt and plugins dependency update (#1337)

Signed-off-by: Hyperledger Bot <hyperledger-bot@hyperledger.org>
Signed-off-by: Yurii Shynbuiev <yurii.shynbuiev@iohk.io>
Co-authored-by: Hyperledger Bot <hyperledger-bot@hyperledger.org>
Co-authored-by: Yurii Shynbuiev <yurii.shynbuiev@iohk.io>

7406 of 15269 relevant lines covered (48.5%)

0.49 hits per line

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

55.93
/castor/src/main/scala/org/hyperledger/identus/castor/core/service/DIDService.scala
1
package org.hyperledger.identus.castor.core.service
2

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

24
import java.time.Instant
25
import scala.collection.immutable.ArraySeq
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] =
37
    ZLayer.fromFunction(DIDServiceImpl(_, _))
1✔
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(signedOperations = Seq(signedOperation.toProto))
×
48
    for {
×
49
      _ <- ZIO
×
50
        .fromEither(didOpValidator.validate(signedOperation.operation))
×
51
        .mapError(DIDOperationError.ValidationError.apply)
52
      operationOutput <- ZIO
×
53
        .fromFuture(_ => nodeClient.scheduleOperations(operationRequest))
×
54
        .mapBoth(ex => DIDOperationError.DLTProxyError("Error scheduling Node operation", ex), _.outputs.toList)
×
55
        .map {
56
          case output :: Nil => Right(output)
×
57
          case _ => Left(DIDOperationError.UnexpectedDLTResult("operation result is expected to have exactly 1 output"))
×
58
        }
59
        .absolve
60
      operationId <- ZIO.fromEither {
×
61
        operationOutput.operationMaybe match {
62
          case OperationMaybe.OperationId(id) => Right(id.toByteArray)
×
63
          case OperationMaybe.Empty =>
×
64
            Left(DIDOperationError.UnexpectedDLTResult("operation result does not contain operation detail"))
×
65
          case OperationMaybe.Error(e) =>
×
66
            Left(DIDOperationError.UnexpectedDLTResult(s"operation result was not successful: $e"))
×
67
        }
68
      }
69
    } yield ScheduleDIDOperationOutcome(
70
      did = signedOperation.operation.did,
×
71
      operation = signedOperation.operation,
72
      operationId = ArraySeq.from(operationId)
×
73
    )
74
  }
75

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

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

125
  // FIXME: This doesn't play well detecting timestamp context and revoked service due to
126
  // the response from Node missing the ledger data for those items.
127
  private def getMinMaxLedgerTime(didData: node_models.DIDData): (Option[Instant], Option[Instant]) = {
1✔
128
    val ledgerTimes = didData.publicKeys.flatMap(_.addedOn) ++
1✔
129
      didData.publicKeys.flatMap(_.revokedOn) ++
1✔
130
      didData.services.flatMap(_.addedOn) ++
1✔
131
      didData.services.flatMap(_.deletedOn)
1✔
132
    val instants = ledgerTimes.flatMap(_.toInstant)
×
133
    (instants.minOption, instants.maxOption)
1✔
134
  }
135

136
  private def extractUnpublishedDIDData(did: LongFormPrismDID): IO[DIDResolutionError, (DIDMetadata, DIDData)] = {
1✔
137
    ZIO
1✔
138
      .fromEither(did.createOperation)
1✔
139
      .mapError(e => DIDResolutionError.ValidationError(OperationValidationError.InvalidArgument(e)))
140
      .flatMap { op =>
141
        // unpublished CreateOperation (if exists) must be validated before the resolution
142
        ZIO
1✔
143
          .fromEither(didOpValidator.validate(op))
1✔
144
          .mapError(DIDResolutionError.ValidationError.apply)
145
          .as(op)
146
      }
147
      .map { op =>
148
        val metadata =
149
          DIDMetadata(
150
            lastOperationHash = ArraySeq.from(did.stateHash.toByteArray),
1✔
151
            canonicalId = None, // unpublished DID must not contain canonicalId
152
            deactivated = false, // unpublished DID cannot be deactivated
153
            created = None, // unpublished DID cannot have timestamp
154
            updated = None // unpublished DID cannot have timestamp
155
          )
156
        val didData = DIDData(
157
          id = did.asCanonical,
1✔
158
          publicKeys = op.publicKeys.collect { case pk: PublicKey => pk },
1✔
159
          services = op.services,
160
          internalKeys = op.publicKeys.collect { case pk: InternalPublicKey => pk },
1✔
161
          context = op.context
162
        )
163
        metadata -> didData
1✔
164
      }
165
  }
166

167
}
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