• 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

43.66
/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/CredentialSchema.scala
1
package io.iohk.atala.pollux.core.model
2

3
import com.fasterxml.jackson.databind.ObjectMapper
4
import com.fasterxml.jackson.databind.json.JsonMapper
5
import com.networknt.schema.*
6
import io.circe.Json
7
import io.iohk.atala.pollux.core.model.error.CredentialSchemaError
8
import io.iohk.atala.pollux.core.model.error.CredentialSchemaError.*
9
import io.iohk.atala.pollux.core.service.{URIDereferencer, URIDereferencerError}
10
import zio.*
×
11
import zio.json.*
×
12

13
import java.net.URI
1✔
14
import java.time.{OffsetDateTime, ZoneOffset}
15
import java.util.UUID
16

17
type Schema = zio.json.ast.Json
18

19
/** @param guid
20
  *   Globally unique identifier of the CredentialSchema object It's calculated as a UUID from string that contains the
21
  *   following fields: author, id and version
22
  * @param id
×
23
  *   Locally unique identifier of the CredentialSchema. It is UUID When the version of the credential schema changes
24
  *   this `id` keeps the same value
25
  * @param name
1✔
26
  *   Human readable name of the CredentialSchema
27
  * @param version
28
  *   Version of the CredentialSchema
29
  * @param author
×
30
  *   DID of the CredentialSchema's author
31
  * @param authored
32
  *   Datetime stamp of the schema creation
33
  * @param tags
34
  *   Tags of the CredentialSchema used for convenient lookup
35
  * @param description
36
  *   Human readable description of the schema
37
  * @param schema
38
  *   Internal schema object that depends on concrete implementation For W3C JsonSchema it is a JsonSchema object For
39
  *   AnonCreds schema is a AnonCreds schema
40
  */
41
case class CredentialSchema(
42
    guid: UUID,
43
    id: UUID,
44
    name: String,
45
    version: String,
46
    author: String,
47
    authored: OffsetDateTime,
×
48
    tags: Seq[String],
49
    description: String,
50
    `type`: String,
51
    schema: Schema
52
) {
1✔
53
  def longId = CredentialSchema.makeLongId(author, id, version)
54
}
55

56
object CredentialSchema {
57

1✔
58
  def makeLongId(author: String, id: UUID, version: String) =
1✔
59
    s"$author/${id.toString}?version=${version}"
1✔
60
  def makeGUID(author: String, id: UUID, version: String) =
1✔
61
    UUID.nameUUIDFromBytes(makeLongId(author, id, version).getBytes)
1✔
62
  def make(in: Input): ZIO[Any, Nothing, CredentialSchema] = {
1✔
63
    for {
×
64
      id <- zio.Random.nextUUID
1✔
65
      cs <- make(id, in)
66
    } yield cs
67
  }
1✔
68
  def make(id: UUID, in: Input): ZIO[Any, Nothing, CredentialSchema] = {
1✔
69
    for {
×
70
      ts <- zio.Clock.currentDateTime.map(
×
71
        _.atZoneSameInstant(ZoneOffset.UTC).toOffsetDateTime
72
      )
×
73
      guid = makeGUID(in.author, id, in.version)
74
    } yield CredentialSchema(
75
      guid = guid,
76
      id = id,
77
      name = in.name,
78
      version = in.version,
79
      author = in.author,
80
      authored = ts,
81
      tags = in.tags,
82
      description = in.description,
83
      `type` = in.`type`,
84
      schema = in.schema
85
    )
86
  }
87

88
  val defaultAgentDid = "did:prism:agent"
89

90
  case class Input(
91
      name: String,
92
      version: String,
93
      description: String,
94
      authored: Option[OffsetDateTime],
95
      tags: Seq[String],
×
96
      author: String = defaultAgentDid,
97
      `type`: String,
98
      schema: Schema
99
  )
100

101
  case class Filter(
×
102
      author: Option[String] = None,
×
103
      name: Option[String] = None,
×
104
      version: Option[String] = None,
×
105
      tags: Option[String] = None
106
  )
107

108
  case class FilteredEntries(entries: Seq[CredentialSchema], count: Long, totalCount: Long)
109

×
110
  given JsonEncoder[CredentialSchema] = DeriveJsonEncoder.gen[CredentialSchema]
×
111
  given JsonDecoder[CredentialSchema] = DeriveJsonDecoder.gen[CredentialSchema]
112

113
  val VC_JSON_SCHEMA_URI = "https://w3c-ccg.github.io/vc-json-schemas/schema/2.0/schema.json"
114

1✔
115
  def validateClaims(
116
      schemaId: String,
117
      claims: String,
118
      uriDereferencer: URIDereferencer
119
  ): IO[CredentialSchemaError, Unit] = {
1✔
120
    for {
×
121
      uri <- ZIO.attempt(new URI(schemaId)).mapError(t => URISyntaxError(t.getMessage))
1✔
122
      content <- uriDereferencer.dereference(uri).mapError(err => UnexpectedError(err.toString))
1✔
123
      vcSchema <- parseAndValidateCredentialSchema(content)
×
124
      jsonSchema <- validateJsonSchema(vcSchema.schema)
1✔
125
      _ <- validateClaims(jsonSchema, claims)
126
    } yield ()
127
  }
128

1✔
129
  def parseAndValidateCredentialSchema(vcSchemaString: String): IO[CredentialSchemaError, CredentialSchema] = {
1✔
130
    for {
×
131
      vcSchema <- vcSchemaString.fromJson[CredentialSchema] match
×
132
        case Left(error)     => ZIO.fail(CredentialSchemaParsingError(s"VC Schema parsing error: $error"))
×
133
        case Right(vcSchema) => ZIO.succeed(vcSchema)
1✔
134
      _ <- validateCredentialSchema(vcSchema)
135
    } yield vcSchema
136
  }
137

1✔
138
  def validateCredentialSchema(vcSchema: CredentialSchema): IO[CredentialSchemaError, Unit] = for {
1✔
139
    _ <-
×
140
      if (vcSchema.`type` == VC_JSON_SCHEMA_URI) ZIO.unit
141
      else ZIO.fail(UnsupportedCredentialSchemaType(s"VC Schema type should be $VC_JSON_SCHEMA_URI"))
1✔
142
    _ <- validateJsonSchema(vcSchema.schema)
143
  } yield ()
144

1✔
145
  def validateJsonSchema(jsonSchema: Schema): IO[CredentialSchemaError, JsonSchema] = {
1✔
146
    for {
×
147
      mapper <- ZIO.attempt(new ObjectMapper()).mapError(t => UnexpectedError(t.getMessage))
1✔
148
      jsonSchemaNode <- ZIO
×
149
        .attempt(mapper.readTree(jsonSchema.toString()))
×
150
        .mapError(t => JsonSchemaParsingError(t.getMessage))
×
151
      specVersion <- ZIO
×
152
        .attempt(SpecVersionDetector.detect(jsonSchemaNode))
×
153
        .mapError(t => UnexpectedError(t.getMessage))
1✔
154
      _ <-
155
        if (specVersion != SpecVersion.VersionFlag.V202012)
156
          ZIO.fail(UnsupportedJsonSchemaSpecVersion(s"Version should be ${JsonMetaSchema.getV202012.getUri}"))
×
157
        else ZIO.unit
×
158
      factory <- ZIO
×
159
        .attempt(JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(specVersion)).objectMapper(mapper).build)
×
160
        .mapError(t => UnexpectedError(t.getMessage))
×
161
      jsonSchema <- ZIO.attempt(factory.getSchema(jsonSchemaNode)).mapError(t => UnexpectedError(t.getMessage))
162
    } yield jsonSchema
163
  }
164

1✔
165
  def validateClaims(jsonSchema: JsonSchema, claims: String): IO[CredentialSchemaError, Unit] = {
166
    import scala.jdk.CollectionConverters.*
1✔
167
    for {
×
168
      mapper <- ZIO.attempt(new ObjectMapper()).mapError(t => UnexpectedError(t.getMessage))
169

170
      // Convert claims to JsonNode
×
171
      jsonClaims <- ZIO
×
172
        .attempt(mapper.readTree(claims))
×
173
        .mapError(t => ClaimsParsingError(t.getMessage))
174

175
      // Validate claims JsonNode
1✔
176
      validationMessages <- ZIO
×
177
        .attempt(jsonSchema.validate(jsonClaims).asScala.toSeq)
×
178
        .mapError(t => ClaimsValidationError(Seq(t.getMessage)))
179

1✔
180
      validationResult <-
×
181
        if (validationMessages.isEmpty) ZIO.unit
182
        else ZIO.fail(ClaimsValidationError(validationMessages.map(_.getMessage)))
183
    } yield validationResult
184
  }
185

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