Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: migrate from Circe to ZIO Json #1471

Merged
merged 24 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fd5dccd
feat: migrate mercury 'models'
bvoiturier Nov 28, 2024
2cd97c5
feat: migrate protocol-connection
bvoiturier Nov 28, 2024
704cdfb
feat: migrate protocol-invitation
bvoiturier Nov 28, 2024
62484b9
feat: migrate protocol-present-proof
bvoiturier Nov 28, 2024
aec2040
feat: migrate protocol-report-problem
bvoiturier Nov 28, 2024
b8b9879
feat: migrate protocol-routing
bvoiturier Nov 28, 2024
95cc961
feat: migrate protocol-revocation-notification
bvoiturier Nov 28, 2024
e22cd99
feat: migrate protocol-trust-ping
bvoiturier Nov 28, 2024
f5cfd97
feat: migrate protocol-issue-credential
bvoiturier Nov 28, 2024
bce688b
feat: migrate protocol-coordinate-mediation
bvoiturier Nov 28, 2024
23005b2
feat: migrate 'agent'
bvoiturier Nov 28, 2024
eef0b8c
feat: migrate agent-didcommx
bvoiturier Nov 29, 2024
f756c26
feat: migrate connect-core
bvoiturier Nov 29, 2024
c9107eb
migrate connect-sql-doobie
bvoiturier Nov 29, 2024
8d4e2f6
feat: migrate pollux-core
bvoiturier Nov 29, 2024
94a515a
feat: migrate pollux-sql-doobie
bvoiturier Nov 29, 2024
f540223
feat: migrate cloud-agent-server
bvoiturier Dec 3, 2024
df11b64
feat: migrate castor
bvoiturier Dec 3, 2024
3f3c16f
Merge branch 'main' into feat/migrate-mercury-to-zio-json
bvoiturier Dec 3, 2024
9335549
feat: migrate mercury-resolver
bvoiturier Dec 3, 2024
a097cb4
feat: migrate pollux-prex
bvoiturier Dec 3, 2024
c517f9d
Merge branch 'main' into feat/migrate-from-circe-to-zio-json
bvoiturier Dec 4, 2024
09767ce
chore: run scalafmt
bvoiturier Dec 4, 2024
e2a9919
fix: fix Json serialisation error when packing forward message for me…
bvoiturier Dec 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,33 +1,16 @@
package org.hyperledger.identus.castor.core.model

import com.google.protobuf.ByteString
import io.circe.Json
import io.iohk.atala.prism.protos.{common_models, node_api, node_models}
import io.iohk.atala.prism.protos.{node_api, node_models}
import io.iohk.atala.prism.protos.common_models.OperationStatus
import io.iohk.atala.prism.protos.node_models.KeyUsage
import io.iohk.atala.prism.protos.node_models.PublicKey.KeyData
import org.hyperledger.identus.castor.core.model.did.{
DIDData,
EllipticCurve,
InternalKeyPurpose,
InternalPublicKey,
PrismDID,
PrismDIDOperation,
PublicKey,
PublicKeyData,
ScheduledDIDOperationDetail,
ScheduledDIDOperationStatus,
Service,
ServiceEndpoint,
ServiceType,
SignedPrismDIDOperation,
UpdateDIDAction,
VerificationRelationship
}
import org.hyperledger.identus.castor.core.model.did.*
import org.hyperledger.identus.castor.core.model.did.ServiceEndpoint.{value, UriOrJsonEndpoint}
import org.hyperledger.identus.shared.models.{Base64UrlString, KeyId}
import org.hyperledger.identus.shared.utils.Traverse.*
import zio.*
import zio.json.{DecoderOps, EncoderOps}
import zio.json.ast.Json

import java.time.Instant
import scala.language.implicitConversions
Expand Down Expand Up @@ -191,8 +174,8 @@ private[castor] trait ProtoModelHelper {
serviceType match {
case ServiceType.Single(name) => name.value
case ts: ServiceType.Multiple =>
val names = ts.values.map(_.value).map(Json.fromString)
Json.arr(names*).noSpaces
val names = ts.values.map(_.value).map(Json.Str(_))
Json.Arr(names*).toJson
}
}
}
Expand All @@ -203,14 +186,14 @@ private[castor] trait ProtoModelHelper {
case ServiceEndpoint.Single(value) =>
value match {
case UriOrJsonEndpoint.Uri(uri) => uri.value
case UriOrJsonEndpoint.Json(json) => Json.fromJsonObject(json).noSpaces
case UriOrJsonEndpoint.Json(json) => json.toJson
}
case endpoints: ServiceEndpoint.Multiple =>
val uris = endpoints.values.map {
case UriOrJsonEndpoint.Uri(uri) => Json.fromString(uri.value)
case UriOrJsonEndpoint.Json(json) => Json.fromJsonObject(json)
case UriOrJsonEndpoint.Uri(uri) => Json.Str(uri.value)
case UriOrJsonEndpoint.Json(json) => json
}
Json.arr(uris*).noSpaces
Json.Arr(uris*).toJson
}
}
}
Expand Down Expand Up @@ -356,24 +339,24 @@ private[castor] trait ProtoModelHelper {

def parseServiceType(s: String): Either[String, ServiceType] = {
// The type field MUST be a string or a non-empty JSON array of strings.
val parsedJson: Option[Either[String, ServiceType.Multiple]] = io.circe.parser
.parse(s)
.toOption // it's OK to let parsing fail (e.g. LinkedDomains without quote is not a JSON string)
.flatMap(_.asArray)
.map { jsonArr =>
jsonArr
.traverse(_.asString.toRight("the service type is not a JSON array of strings"))
.flatMap(_.traverse(ServiceType.Name.fromString))
.map(_.toList)
.flatMap {
case head :: tail => Right(ServiceType.Multiple(head, tail))
case Nil => Left("the service type cannot be an empty JSON array")
}
.filterOrElse(
_ => s == io.circe.Json.arr(jsonArr*).noSpaces,
"the service type is a valid JSON array of strings, but not conform to the ABNF"
)
}
val parsedJson: Option[Either[String, ServiceType.Multiple]] =
s.fromJson[Json]
.toOption // it's OK to let parsing fail (e.g. LinkedDomains without quote is not a JSON string)
.flatMap(_.asArray)
.map { jsonArr =>
jsonArr
.traverse(_.asString.toRight("the service type is not a JSON array of strings"))
.flatMap(_.traverse(ServiceType.Name.fromString))
.map(_.toList)
.flatMap {
case head :: tail => Right(ServiceType.Multiple(head, tail))
case Nil => Left("the service type cannot be an empty JSON array")
}
.filterOrElse(
_ => s == Json.Arr(jsonArr*).toJson,
"the service type is a valid JSON array of strings, but not conform to the ABNF"
)
}

parsedJson match {
// serviceType is a valid JSON array of strings
Expand All @@ -391,22 +374,22 @@ private[castor] trait ProtoModelHelper {
* 2. a JSON object
* 3. a non-empty JSON array of URIs and/or JSON objects
*/
val parsedJson: Option[Either[String, ServiceEndpoint]] = io.circe.parser
.parse(s)
.toOption // it's OK to let parsing fail (e.g. http://example.com without quote is not a JSON string)
.flatMap { json =>
val parsedObject = json.asObject.map(obj => Right(ServiceEndpoint.Single(obj)))
val parsedArray = json.asArray.map(_.traverse[String, UriOrJsonEndpoint] { js =>
val obj = js.asObject.map(obj => Right(obj: UriOrJsonEndpoint))
val str = js.asString.map(str => ServiceEndpoint.UriValue.fromString(str).map[UriOrJsonEndpoint](i => i))
obj.orElse(str).getOrElse(Left("the service endpoint is not a JSON array of URIs and/or JSON objects"))
}.map(_.toList).flatMap {
case head :: tail => Right(ServiceEndpoint.Multiple(head, tail))
case Nil => Left("the service endpoint cannot be an empty JSON array")
})
val parsedJson: Option[Either[String, ServiceEndpoint]] =
s.fromJson[Json]
.toOption // it's OK to let parsing fail (e.g. http://example.com without quote is not a JSON string)
.flatMap { json =>
val parsedObject = json.asObject.map(obj => Right(ServiceEndpoint.Single(obj)))
val parsedArray = json.asArray.map(_.traverse[String, UriOrJsonEndpoint] { js =>
val obj = js.asObject.map(obj => Right(obj: UriOrJsonEndpoint))
val str = js.asString.map(str => ServiceEndpoint.UriValue.fromString(str).map[UriOrJsonEndpoint](i => i))
obj.orElse(str).getOrElse(Left("the service endpoint is not a JSON array of URIs and/or JSON objects"))
}.map(_.toList).flatMap {
case head :: tail => Right(ServiceEndpoint.Multiple(head, tail))
case Nil => Left("the service endpoint cannot be an empty JSON array")
})

parsedObject.orElse(parsedArray)
}
parsedObject.orElse(parsedArray)
}

parsedJson match {
// serviceEndpoint is a valid JSON object or array
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.hyperledger.identus.castor.core.model.did

import io.circe.JsonObject
import org.hyperledger.identus.castor.core.util.UriUtils
import zio.json.ast.Json as ZioJson

sealed trait ServiceEndpoint {
def normalize(): ServiceEndpoint
Expand Down Expand Up @@ -35,12 +35,12 @@ object ServiceEndpoint {
override def normalize(): UriOrJsonEndpoint = copy(uri = uri.normalize())
}

final case class Json(json: JsonObject) extends UriOrJsonEndpoint {
final case class Json(json: ZioJson.Obj) extends UriOrJsonEndpoint {
override def normalize(): UriOrJsonEndpoint = this
}

given Conversion[UriValue, UriOrJsonEndpoint] = Uri(_)
given Conversion[JsonObject, UriOrJsonEndpoint] = Json(_)
given Conversion[ZioJson.Obj, UriOrJsonEndpoint] = Json(_)
}

final case class Single(value: UriOrJsonEndpoint) extends ServiceEndpoint {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.hyperledger.identus.castor.core.model.did.w3c

import io.circe.Json
import zio.json.ast.Json

/** A projection of DIDDocument data model to W3C compliant DID representation */
final case class DIDDocumentRepr(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package org.hyperledger.identus.castor.core.model.did.w3c

import io.circe.Json
import org.hyperledger.identus.castor.core.model.did.*
import org.hyperledger.identus.castor.core.model.did.ServiceEndpoint.UriOrJsonEndpoint
import org.hyperledger.identus.shared.crypto.Apollo
import org.hyperledger.identus.shared.models.{Base64UrlString, HexString}
import zio.json.ast.Json

import java.time.{Instant, ZoneOffset}
import java.time.format.DateTimeFormatter
Expand Down Expand Up @@ -95,15 +95,15 @@ private[castor] trait W3CModelHelper {
serviceEndpoint match {
case ServiceEndpoint.Single(uri) =>
uri match {
case UriOrJsonEndpoint.Uri(uri) => Json.fromString(uri.value)
case UriOrJsonEndpoint.Json(json) => Json.fromJsonObject(json)
case UriOrJsonEndpoint.Uri(uri) => Json.Str(uri.value)
case UriOrJsonEndpoint.Json(json) => json
}
case ep: ServiceEndpoint.Multiple =>
val uris = ep.values.map {
case UriOrJsonEndpoint.Uri(uri) => Json.fromString(uri.value)
case UriOrJsonEndpoint.Json(json) => Json.fromJsonObject(json)
case UriOrJsonEndpoint.Uri(uri) => Json.Str(uri.value)
case UriOrJsonEndpoint.Json(json) => json
}
Json.arr(uris*)
Json.Arr(uris*)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package org.hyperledger.identus.castor.core.model

import com.google.protobuf.timestamp.Timestamp
import io.circe.{Json, JsonObject}
import io.iohk.atala.prism.protos.common_models.Ledger
import io.iohk.atala.prism.protos.node_models
import org.hyperledger.identus.castor.core.model.did.{ServiceEndpoint, ServiceType}
import org.hyperledger.identus.castor.core.model.did.ServiceEndpoint.{UriOrJsonEndpoint, UriValue}
import org.hyperledger.identus.castor.core.util.GenUtils
import zio.*
import zio.json.ast.Json
import zio.test.*
import zio.test.Assertion.*

Expand All @@ -20,9 +20,9 @@ object ProtoModelHelperSpec extends ZIOSpecDefault {

given Conversion[String, ServiceType.Name] = ServiceType.Name.fromStringUnsafe
given Conversion[String, UriOrJsonEndpoint] = s => UriOrJsonEndpoint.Uri(UriValue.fromString(s).toOption.get)
given Conversion[JsonObject, UriOrJsonEndpoint] = UriOrJsonEndpoint.Json(_)
given Conversion[Json.Obj, UriOrJsonEndpoint] = UriOrJsonEndpoint.Json(_)
given Conversion[String, ServiceEndpoint] = s => ServiceEndpoint.Single(s)
given Conversion[JsonObject, ServiceEndpoint] = json => ServiceEndpoint.Single(UriOrJsonEndpoint.Json(json))
given Conversion[Json.Obj, ServiceEndpoint] = json => ServiceEndpoint.Single(UriOrJsonEndpoint.Json(json))

private def makePublicKey(id: String, revokedOn: Option[node_models.LedgerData] = None): node_models.PublicKey =
node_models.PublicKey(
Expand Down Expand Up @@ -351,7 +351,7 @@ object ProtoModelHelperSpec extends ZIOSpecDefault {
test("parse valid json object") {
val serviceEndpoint = """{"uri": "https://example.com"}"""
val result = ProtoModelHelper.parseServiceEndpoint(serviceEndpoint)
val expected: ServiceEndpoint = Json.obj("uri" -> Json.fromString("https://example.com")).asObject.get
val expected: ServiceEndpoint = Json.Obj("uri" -> Json.Str("https://example.com")).asObject.get
assert(result)(isRight(equalTo(expected)))
},
test("parse invalid endpoint that is not a string or object") {
Expand All @@ -362,7 +362,7 @@ object ProtoModelHelperSpec extends ZIOSpecDefault {
test("parse empty json object") {
val serviceEndpoint = "{}"
val result = ProtoModelHelper.parseServiceEndpoint(serviceEndpoint)
val expected: ServiceEndpoint = Json.obj().asObject.get
val expected: ServiceEndpoint = Json.Obj().asObject.get
assert(result)(isRight(equalTo(expected)))
},
test("parse empty json array") {
Expand All @@ -389,9 +389,9 @@ object ProtoModelHelperSpec extends ZIOSpecDefault {
val serviceEndpoint = """[{"uri": "https://example.com"}, {"uri": "https://example2.com"}]"""
val result = ProtoModelHelper.parseServiceEndpoint(serviceEndpoint)
val expected = ServiceEndpoint.Multiple(
Json.obj("uri" -> Json.fromString("https://example.com")).asObject.get,
Json.Obj("uri" -> Json.Str("https://example.com")).asObject.get,
Seq(
Json.obj("uri" -> Json.fromString("https://example2.com")).asObject.get
Json.Obj("uri" -> Json.Str("https://example2.com")).asObject.get
)
)
assert(result)(isRight(equalTo(expected)))
Expand All @@ -400,7 +400,7 @@ object ProtoModelHelperSpec extends ZIOSpecDefault {
val serviceEndpoint = """[{"uri": "https://example.com"}, "https://example2.com"]"""
val result = ProtoModelHelper.parseServiceEndpoint(serviceEndpoint)
val expected = ServiceEndpoint.Multiple(
Json.obj("uri" -> Json.fromString("https://example.com")).asObject.get,
Json.Obj("uri" -> Json.Str("https://example.com")).asObject.get,
Seq("https://example2.com")
)
assert(result)(isRight(equalTo(expected)))
Expand Down Expand Up @@ -428,7 +428,7 @@ object ProtoModelHelperSpec extends ZIOSpecDefault {
assert(encoded)(equalTo("http://example.com"))
},
test("encode single endoint JSON object") {
val uri: UriOrJsonEndpoint = JsonObject("uri" -> Json.fromString("http://example.com"))
val uri: UriOrJsonEndpoint = Json.Obj("uri" -> Json.Str("http://example.com"))
val serviceEndpoint = ServiceEndpoint.Single(uri)
val encoded = serviceEndpoint.toProto
assert(encoded)(equalTo("""{"uri":"http://example.com"}"""))
Expand All @@ -441,8 +441,8 @@ object ProtoModelHelperSpec extends ZIOSpecDefault {
assert(encoded)(equalTo("""["http://example.com","http://example2.com"]"""))
},
test("encode multiple endoints JSON object") {
val uri: UriOrJsonEndpoint = JsonObject("uri" -> Json.fromString("http://example.com"))
val uri2: UriOrJsonEndpoint = JsonObject("uri" -> Json.fromString("http://example2.com"))
val uri: UriOrJsonEndpoint = Json.Obj("uri" -> Json.Str("http://example.com"))
val uri2: UriOrJsonEndpoint = Json.Obj("uri" -> Json.Str("http://example2.com"))
val serviceEndpoint = ServiceEndpoint.Multiple(uri, Seq(uri2))
val encoded = serviceEndpoint.toProto
assert(encoded)(equalTo("""[{"uri":"http://example.com"},{"uri":"http://example2.com"}]"""))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package org.hyperledger.identus.castor.core.util

import io.circe.Json
import org.hyperledger.identus.castor.core.model.did.*
import org.hyperledger.identus.castor.core.model.did.ServiceEndpoint.{UriOrJsonEndpoint, UriValue}
import org.hyperledger.identus.shared.crypto.Apollo
import org.hyperledger.identus.shared.models.{Base64UrlString, KeyId}
import zio.*
import zio.json.ast.Json
import zio.test.Gen

import scala.language.implicitConversions
Expand Down Expand Up @@ -62,7 +62,7 @@ object GenUtils {
)
sampleUri = "https://example.com"
uriEndpointGen = Gen.const(UriOrJsonEndpoint.Uri(UriValue.fromString(sampleUri).toOption.get))
jsonEndpointGen = Gen.const(UriOrJsonEndpoint.Json(Json.obj("uri" -> Json.fromString(sampleUri)).asObject.get))
jsonEndpointGen = Gen.const(UriOrJsonEndpoint.Json(Json.Obj("uri" -> Json.Str(sampleUri)).asObject.get))
endpoints <- Gen.oneOf[Any, ServiceEndpoint](
uriEndpointGen.map(ServiceEndpoint.Single(_)),
jsonEndpointGen.map(ServiceEndpoint.Single(_)),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package org.hyperledger.identus.agent.server.http

import io.circe.*
import io.circe.generic.semiauto.*
import io.circe.syntax.*
import io.micrometer.prometheusmetrics.PrometheusMeterRegistry
import org.http4s.*
import org.http4s.blaze.server.BlazeServerBuilder
Expand All @@ -18,6 +15,7 @@ import sttp.tapir.server.metrics.MetricLabels
import sttp.tapir.ztapir.ZServerEndpoint
import zio.*
import zio.interop.catz.*
import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, EncoderOps, JsonDecoder, JsonEncoder}

class ZHttp4sBlazeServer(micrometerRegistry: PrometheusMeterRegistry, metricsNamespace: String) {

Expand All @@ -34,9 +32,8 @@ class ZHttp4sBlazeServer(micrometerRegistry: PrometheusMeterRegistry, metricsNam
secChUaPlatform: Option[String],
)
object FingerPrintData {
given encoder: Encoder[FingerPrintData] = deriveEncoder[FingerPrintData]

given decoder: Decoder[FingerPrintData] = deriveDecoder[FingerPrintData]
given encoder: JsonEncoder[FingerPrintData] = DeriveJsonEncoder.gen
given decoder: JsonDecoder[FingerPrintData] = DeriveJsonDecoder.gen
}

val headers = sr.headers
Expand All @@ -52,7 +49,7 @@ class ZHttp4sBlazeServer(micrometerRegistry: PrometheusMeterRegistry, metricsNam
headers.find(_.name.toLowerCase == "sec-ch-ua-platform").map(_.value),
)

val jsonStr = fingerPrintData.asJson.dropNullValues.spaces2
val jsonStr = fingerPrintData.toJson
val canonicalized = Json.canonicalizeToJcs(jsonStr).toOption

canonicalized.map(x => Sha256Hash.compute(x.getBytes))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package org.hyperledger.identus.agent.server.jobs

import cats.syntax.all.*
import io.circe.parser.*
import io.circe.syntax.*
import org.hyperledger.identus.agent.server.config.AppConfig
import org.hyperledger.identus.agent.server.jobs.BackgroundJobError.{
ErrorResponseReceivedFromPeerAgent,
Expand Down Expand Up @@ -37,6 +35,7 @@ import org.hyperledger.identus.shared.models.{Failure, *}
import org.hyperledger.identus.shared.utils.aspects.CustomMetricsAspect
import org.hyperledger.identus.shared.utils.DurationOps.toMetricsSeconds
import zio.*
import zio.json.{DecoderOps, EncoderOps}
import zio.metrics.*
import zio.prelude.{Validation, ZValidation}
import zio.prelude.ZValidation.{Failure as ZFailure, *}
Expand Down Expand Up @@ -1105,13 +1104,12 @@ object PresentBackgroundJobs extends BackgroundJobsHelper {
val maybePresentationOptions: Either[PresentationError, Option[Options]] =
requestPresentation.attachments.headOption
.map(attachment =>
decode[JsonData](
attachment.data.asJson.noSpaces
)
attachment.data.toJson
.fromJson[JsonData]
.leftMap(err => PresentationDecodingError(s"JsonData decoding error: $err"))
.flatMap(data =>
org.hyperledger.identus.pollux.core.model.presentation.PresentationAttachment.given_Decoder_PresentationAttachment
.decodeJson(data.json.asJson)
org.hyperledger.identus.pollux.core.model.presentation.PresentationAttachment.given_JsonDecoder_PresentationAttachment
.decodeJson(data.json.toJson)
.map(_.options)
.leftMap(err => PresentationDecodingError(s"PresentationAttachment decoding error: $err"))
)
Expand Down
Loading
Loading