From 1147ab852341390d4a1f988456f19160976a9de2 Mon Sep 17 00:00:00 2001 From: Kendall Weihe Date: Tue, 20 Aug 2024 11:28:15 -0400 Subject: [PATCH] Add PortableDid from_json_string() and to_json_string() --- bindings/web5_uniffi/src/web5.udl | 4 +- .../src/dids/portable_did.rs | 15 ++++-- bindings/web5_uniffi_wrapper/src/errors.rs | 7 --- .../main/kotlin/web5/sdk/dids/PortableDid.kt | 33 ++++++++---- .../src/main/kotlin/web5/sdk/rust/UniFFI.kt | 51 +++++++++++++++---- .../kotlin/web5/sdk/dids/PortableDidTest.kt | 17 ++++++- crates/web5/src/dids/portable_did.rs | 28 +++------- crates/web5_cli/src/vcs.rs | 6 ++- docs/API_DESIGN.md | 4 +- 9 files changed, 104 insertions(+), 61 deletions(-) diff --git a/bindings/web5_uniffi/src/web5.udl b/bindings/web5_uniffi/src/web5.udl index c633b640..2bfcc653 100644 --- a/bindings/web5_uniffi/src/web5.udl +++ b/bindings/web5_uniffi/src/web5.udl @@ -208,9 +208,11 @@ dictionary PortableDidData { }; interface PortableDid { - [Throws=Web5Error] + [Name=from_json_string, Throws=Web5Error] constructor([ByRef] string json); PortableDidData get_data(); + [Throws=Web5Error] + string to_json_string(); }; dictionary BearerDidData { diff --git a/bindings/web5_uniffi_wrapper/src/dids/portable_did.rs b/bindings/web5_uniffi_wrapper/src/dids/portable_did.rs index fa8c1ce4..8f272547 100644 --- a/bindings/web5_uniffi_wrapper/src/dids/portable_did.rs +++ b/bindings/web5_uniffi_wrapper/src/dids/portable_did.rs @@ -1,16 +1,23 @@ -use web5::dids::portable_did::PortableDid as InnerPortableDid; - use crate::errors::Result; +use web5::{ + dids::portable_did::PortableDid as InnerPortableDid, + json::{FromJson, ToJson}, +}; pub struct PortableDid(pub InnerPortableDid); impl PortableDid { - pub fn new(json: &str) -> Result { - let inner_portable_did = InnerPortableDid::new(json)?; + pub fn from_json_string(json: &str) -> Result { + let inner_portable_did = InnerPortableDid::from_json_string(json)?; Ok(Self(inner_portable_did)) } pub fn get_data(&self) -> InnerPortableDid { self.0.clone() } + + pub fn to_json_string(&self) -> Result { + let json_string = self.0.to_json_string()?; + Ok(json_string) + } } diff --git a/bindings/web5_uniffi_wrapper/src/errors.rs b/bindings/web5_uniffi_wrapper/src/errors.rs index 37cb23df..26742aae 100644 --- a/bindings/web5_uniffi_wrapper/src/errors.rs +++ b/bindings/web5_uniffi_wrapper/src/errors.rs @@ -8,7 +8,6 @@ use web5::crypto::dsa::DsaError; use web5::crypto::key_managers::KeyManagerError; use web5::dids::bearer_did::BearerDidError; use web5::dids::methods::MethodError; -use web5::dids::portable_did::PortableDidError; use web5::errors::Web5Error as InnerWeb5Error; #[derive(Debug, Error)] @@ -90,12 +89,6 @@ impl From for Web5Error { } } -impl From for Web5Error { - fn from(error: PortableDidError) -> Self { - Web5Error::new(error) - } -} - impl From for Web5Error { fn from(error: MethodError) -> Self { Web5Error::new(error) diff --git a/bound/kt/src/main/kotlin/web5/sdk/dids/PortableDid.kt b/bound/kt/src/main/kotlin/web5/sdk/dids/PortableDid.kt index 7494064d..2fc1016b 100644 --- a/bound/kt/src/main/kotlin/web5/sdk/dids/PortableDid.kt +++ b/bound/kt/src/main/kotlin/web5/sdk/dids/PortableDid.kt @@ -3,23 +3,34 @@ package web5.sdk.dids import web5.sdk.crypto.keys.Jwk import web5.sdk.rust.PortableDid as RustCorePortableDid -class PortableDid { - val didUri: String - val document: Document - val privateKeys: List - +class PortableDid private constructor( + val didUri: String, + val document: Document, + val privateKeys: List, internal val rustCorePortableDid: RustCorePortableDid - +) { /** * Constructs a PortableDid from a JSON string. * * @param json The JSON string. */ - constructor(json: String) { - this.rustCorePortableDid = RustCorePortableDid(json) + companion object { + fun fromJsonString(json: String): PortableDid { + val rustCorePortableDid = RustCorePortableDid.fromJsonString(json) + val data = rustCorePortableDid.getData() + return PortableDid( + data.didUri, + data.document, + data.privateJwks.map { Jwk.fromRustCoreJwkData(it) }, + rustCorePortableDid + ) + } + } - this.didUri = rustCorePortableDid.getData().didUri - this.document = rustCorePortableDid.getData().document - this.privateKeys = rustCorePortableDid.getData().privateJwks.map {Jwk.fromRustCoreJwkData(it) } + /** + * Serializes a PortableDid to a JSON string. + */ + fun toJsonString(): String { + return rustCorePortableDid.toJsonString() } } \ No newline at end of file diff --git a/bound/kt/src/main/kotlin/web5/sdk/rust/UniFFI.kt b/bound/kt/src/main/kotlin/web5/sdk/rust/UniFFI.kt index c2006916..a66f2cf1 100644 --- a/bound/kt/src/main/kotlin/web5/sdk/rust/UniFFI.kt +++ b/bound/kt/src/main/kotlin/web5/sdk/rust/UniFFI.kt @@ -881,6 +881,8 @@ internal open class UniffiVTableCallbackInterfaceVerifier( + + @@ -1018,10 +1020,12 @@ internal interface UniffiLib : Library { ): Pointer fun uniffi_web5_uniffi_fn_free_portabledid(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, ): Unit - fun uniffi_web5_uniffi_fn_constructor_portabledid_new(`json`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, + fun uniffi_web5_uniffi_fn_constructor_portabledid_from_json_string(`json`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, ): Pointer fun uniffi_web5_uniffi_fn_method_portabledid_get_data(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, ): RustBuffer.ByValue + fun uniffi_web5_uniffi_fn_method_portabledid_to_json_string(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, + ): RustBuffer.ByValue fun uniffi_web5_uniffi_fn_clone_presentationdefinition(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, ): Pointer fun uniffi_web5_uniffi_fn_free_presentationdefinition(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, @@ -1228,6 +1232,8 @@ internal interface UniffiLib : Library { ): Short fun uniffi_web5_uniffi_checksum_method_portabledid_get_data( ): Short + fun uniffi_web5_uniffi_checksum_method_portabledid_to_json_string( + ): Short fun uniffi_web5_uniffi_checksum_method_presentationdefinition_get_json_serialized_presentation_definition( ): Short fun uniffi_web5_uniffi_checksum_method_presentationdefinition_select_credentials( @@ -1268,7 +1274,7 @@ internal interface UniffiLib : Library { ): Short fun uniffi_web5_uniffi_checksum_constructor_jwk_new( ): Short - fun uniffi_web5_uniffi_checksum_constructor_portabledid_new( + fun uniffi_web5_uniffi_checksum_constructor_portabledid_from_json_string( ): Short fun uniffi_web5_uniffi_checksum_constructor_presentationdefinition_new( ): Short @@ -1359,6 +1365,9 @@ private fun uniffiCheckApiChecksums(lib: UniffiLib) { if (lib.uniffi_web5_uniffi_checksum_method_portabledid_get_data() != 27045.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } + if (lib.uniffi_web5_uniffi_checksum_method_portabledid_to_json_string() != 53673.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } if (lib.uniffi_web5_uniffi_checksum_method_presentationdefinition_get_json_serialized_presentation_definition() != 52261.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } @@ -1419,7 +1428,7 @@ private fun uniffiCheckApiChecksums(lib: UniffiLib) { if (lib.uniffi_web5_uniffi_checksum_constructor_jwk_new() != 59888.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_web5_uniffi_checksum_constructor_portabledid_new() != 37852.toShort()) { + if (lib.uniffi_web5_uniffi_checksum_constructor_portabledid_from_json_string() != 64165.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_web5_uniffi_checksum_constructor_presentationdefinition_new() != 13282.toShort()) { @@ -4649,6 +4658,8 @@ public interface PortableDidInterface { fun `getData`(): PortableDidData + fun `toJsonString`(): kotlin.String + companion object } @@ -4669,13 +4680,6 @@ open class PortableDid: Disposable, AutoCloseable, PortableDidInterface { this.pointer = null this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) } - constructor(`json`: kotlin.String) : - this( - uniffiRustCallWithError(Web5Exception) { _status -> - UniffiLib.INSTANCE.uniffi_web5_uniffi_fn_constructor_portabledid_new( - FfiConverterString.lower(`json`),_status) -} - ) protected val pointer: Pointer? protected val cleanable: UniffiCleaner.Cleanable @@ -4753,10 +4757,35 @@ open class PortableDid: Disposable, AutoCloseable, PortableDidInterface { + @Throws(Web5Exception::class)override fun `toJsonString`(): kotlin.String { + return FfiConverterString.lift( + callWithPointer { + uniffiRustCallWithError(Web5Exception) { _status -> + UniffiLib.INSTANCE.uniffi_web5_uniffi_fn_method_portabledid_to_json_string( + it, _status) +} + } + ) + } + + + + companion object { + + @Throws(Web5Exception::class) fun `fromJsonString`(`json`: kotlin.String): PortableDid { + return FfiConverterTypePortableDid.lift( + uniffiRustCallWithError(Web5Exception) { _status -> + UniffiLib.INSTANCE.uniffi_web5_uniffi_fn_constructor_portabledid_from_json_string( + FfiConverterString.lower(`json`),_status) +} + ) + } - companion object + + + } } diff --git a/bound/kt/src/test/kotlin/web5/sdk/dids/PortableDidTest.kt b/bound/kt/src/test/kotlin/web5/sdk/dids/PortableDidTest.kt index f5679ea5..fdacf1cf 100644 --- a/bound/kt/src/test/kotlin/web5/sdk/dids/PortableDidTest.kt +++ b/bound/kt/src/test/kotlin/web5/sdk/dids/PortableDidTest.kt @@ -14,7 +14,7 @@ class PortableDidTest { """.trimIndent() assertDoesNotThrow { - val portableDid = PortableDid(jsonString) + val portableDid = PortableDid.fromJsonString(jsonString) assertEquals("did:web:tbd.website%3A9002:alice", portableDid.didUri) } } @@ -23,7 +23,20 @@ class PortableDidTest { fun `instantiation from json string throws with invalid json string`() { val invalidJsonString = "something not valid" assertThrows(Web5Exception::class.java) { - PortableDid(invalidJsonString) + PortableDid.fromJsonString(invalidJsonString) + } + } + + @Test + fun `can serialize to a json string`() { + val jsonString = """ + {"uri":"did:web:tbd.website%3A9002:alice","document":{"id":"did:web:tbd.website%3A9002:alice","@context":["https://www.w3.org/ns/did/v1"],"verificationMethod":[{"id":"did:web:tbd.website%3A9002:alice#key-0","type":"JsonWebKey","controller":"did:web:tbd.website%3A9002:alice","publicKeyJwk":{"alg":"Ed25519","kty":"OKP","crv":"Ed25519","x":"NNoVSv_v34ombmylF572t9HYYDiJtMgfckRT1W0vW0g"}}]},"privateKeys":[{"alg":"Ed25519","kty":"OKP","crv":"Ed25519","d":"SwuWbL-Fm64OUFy6x3FBt3RiB79RcnZZrllGT24m4BA","x":"NNoVSv_v34ombmylF572t9HYYDiJtMgfckRT1W0vW0g"}]} + """.trimIndent() + + assertDoesNotThrow { + val portableDid = PortableDid.fromJsonString(jsonString) + val serializedJsonString = portableDid.toJsonString() + assertEquals(jsonString, serializedJsonString) } } } \ No newline at end of file diff --git a/crates/web5/src/dids/portable_did.rs b/crates/web5/src/dids/portable_did.rs index c545b441..1745fcb0 100644 --- a/crates/web5/src/dids/portable_did.rs +++ b/crates/web5/src/dids/portable_did.rs @@ -1,7 +1,9 @@ use super::data_model::document::Document; -use crate::crypto::jwk::Jwk; +use crate::{ + crypto::jwk::Jwk, + json::{FromJson, ToJson}, +}; use serde::{Deserialize, Serialize}; -use serde_json::Error as SerdeJsonError; #[derive(Serialize, Deserialize, Clone)] pub struct PortableDid { @@ -12,23 +14,5 @@ pub struct PortableDid { pub private_jwks: Vec, } -#[derive(thiserror::Error, Debug)] -pub enum PortableDidError { - #[error("serde json error {0}")] - SerdeJsonError(String), -} - -impl From for PortableDidError { - fn from(err: SerdeJsonError) -> Self { - PortableDidError::SerdeJsonError(err.to_string()) - } -} - -type Result = std::result::Result; - -impl PortableDid { - pub fn new(json: &str) -> Result { - let portable_did = serde_json::from_str::(json)?; - Ok(portable_did) - } -} +impl FromJson for PortableDid {} +impl ToJson for PortableDid {} diff --git a/crates/web5_cli/src/vcs.rs b/crates/web5_cli/src/vcs.rs index f453f9f0..0275a720 100644 --- a/crates/web5_cli/src/vcs.rs +++ b/crates/web5_cli/src/vcs.rs @@ -6,7 +6,7 @@ use web5::{ CredentialSubject, Issuer, VerifiableCredential, VerifiableCredentialCreateOptions, }, dids::{bearer_did::BearerDid, portable_did::PortableDid}, - json::ToJson, + json::{FromJson, ToJson}, }; #[derive(Subcommand, Debug)] @@ -44,7 +44,9 @@ impl Commands { no_indent, json_escape, } => { - let portable_did = portable_did.as_ref().map(|p| PortableDid::new(p).unwrap()); + let portable_did = portable_did + .as_ref() + .map(|p| PortableDid::from_json_string(p).unwrap()); let issuer = Issuer::String(match issuer { Some(i) => i.to_string(), None => match &portable_did { diff --git a/docs/API_DESIGN.md b/docs/API_DESIGN.md index 8170437a..1f67ff1c 100644 --- a/docs/API_DESIGN.md +++ b/docs/API_DESIGN.md @@ -695,7 +695,9 @@ CLASS PortableDid DATA MEMBER uri: string DATA MEMBER private_jwks: []Jwk DATA MEMBER document: Document - CONSTRUCTOR(json: string) + + CONSTRUCTOR from_json_string(json: string) + METHOD to_json_string(): string ``` ### Example: Create a [`PortableDid`](#portabledid) via the `web5` CLI