diff --git a/clients/js/test/externalPlugins/appData.ts b/clients/js/test/externalPlugins/appData.ts index 96aee756..b1e4eb91 100644 --- a/clients/js/test/externalPlugins/appData.ts +++ b/clients/js/test/externalPlugins/appData.ts @@ -30,8 +30,8 @@ type TestContext = { dataAuthority: PluginAuthority; wrongDataAuthoritySigner?: Signer; wrongDataAuthority?: PluginAuthority; - data: string; - otherData: string; + data: Uint8Array; + otherData: Uint8Array; }; async function generateTestContext( @@ -76,19 +76,21 @@ async function generateTestContext( } } - let data = ''; - let otherData = ''; + let data = new Uint8Array(); + let otherData = new Uint8Array(); + if (schema === ExternalPluginAdapterSchema.Binary) { - data = 'Hello, world!'; - otherData = 'Hello, world! Hello, world!'; + const binaryData = 'Hello, world!'; + const binaryOtherData = 'Hello, world! Hello, world!'; + data = Uint8Array.from(Buffer.from(binaryData)); + otherData = Uint8Array.from(Buffer.from(binaryOtherData)); } else if (schema === ExternalPluginAdapterSchema.Json) { - data = JSON.stringify({ message: 'Hello', target: 'world' }); - otherData = JSON.stringify({ - message: 'Hello hello', - target: 'big wide world', - }); + const dataJson = { message: 'Hello', target: 'world' }; + const otherDataJson = { message: 'Hello hello', target: 'big wide world' }; + data = Uint8Array.from(Buffer.from(JSON.stringify(dataJson))); + otherData = Uint8Array.from(Buffer.from(JSON.stringify(otherDataJson))); } else if (schema === ExternalPluginAdapterSchema.MsgPack) { - data = msgpack.encode({ message: 'Hello', target: 'msgpack' }).toString(); + data = msgpack.encode({ message: 'Hello', target: 'msgpack' }); } if (!dataAuthoritySigner) { @@ -181,7 +183,7 @@ DATA_AUTHORITIES.forEach((dataAuthorityType) => { dataAuthority, }, authority: dataAuthoritySigner, - data: Uint8Array.from(Buffer.from(data)), + data, asset: asset.publicKey, }).sendAndConfirm(umi); @@ -190,9 +192,9 @@ DATA_AUTHORITIES.forEach((dataAuthorityType) => { schema === ExternalPluginAdapterSchema.Binary || schema === ExternalPluginAdapterSchema.MsgPack ) { - assertData = Uint8Array.from(Buffer.from(data)); + assertData = data; } else if (schema === ExternalPluginAdapterSchema.Json) { - assertData = JSON.parse(data); + assertData = JSON.parse(Buffer.from(data).toString()); } await assertAsset(t, umi, { @@ -262,9 +264,9 @@ DATA_AUTHORITIES.forEach((dataAuthorityType) => { schema === ExternalPluginAdapterSchema.Binary || schema === ExternalPluginAdapterSchema.MsgPack ) { - assertData = Uint8Array.from(Buffer.from(data)); + assertData = data; } else if (schema === ExternalPluginAdapterSchema.Json) { - assertData = JSON.parse(data); + assertData = JSON.parse(Buffer.from(data).toString()); } await assertAsset(t, umi, { @@ -296,9 +298,9 @@ DATA_AUTHORITIES.forEach((dataAuthorityType) => { schema === ExternalPluginAdapterSchema.Binary || schema === ExternalPluginAdapterSchema.MsgPack ) { - assertData = Uint8Array.from(Buffer.from(otherData)); + assertData = otherData; } else if (schema === ExternalPluginAdapterSchema.Json) { - assertData = JSON.parse(otherData); + assertData = JSON.parse(Buffer.from(otherData).toString()); } await assertAsset(t, umi, { @@ -452,7 +454,7 @@ test(`updating a plugin before a secure app data does not corrupt the data`, asy asset: asset.publicKey, }).sendAndConfirm(umi); - const assertData = JSON.parse(data); + const assertData = JSON.parse(Buffer.from(data).toString()); await assertAsset(t, umi, { ...DEFAULT_ASSET, diff --git a/clients/js/test/externalPlugins/linkedAppData.test.ts b/clients/js/test/externalPlugins/linkedAppData.test.ts index 0d00a5f6..593de476 100644 --- a/clients/js/test/externalPlugins/linkedAppData.test.ts +++ b/clients/js/test/externalPlugins/linkedAppData.test.ts @@ -1,6 +1,7 @@ import test from 'ava'; import { generateSignerWithSol } from '@metaplex-foundation/umi-bundle-tests'; import { Signer, Umi } from '@metaplex-foundation/umi'; +import * as msgpack from '@msgpack/msgpack'; import { assertAsset, assertCollection, @@ -35,8 +36,8 @@ type TestContext = { dataAuthority: PluginAuthority; wrongDataAuthoritySigner?: Signer; wrongDataAuthority?: PluginAuthority; - data: string; - otherData: string; + data: Uint8Array; + otherData: Uint8Array; }; async function generateTestContext( @@ -81,17 +82,21 @@ async function generateTestContext( } } - let data = ''; - let otherData = ''; + let data = new Uint8Array(); + let otherData = new Uint8Array(); + if (schema === ExternalPluginAdapterSchema.Binary) { - data = 'Hello, world!'; - otherData = 'Hello, world! Hello, world!'; + const binaryData = 'Hello, world!'; + const binaryOtherData = 'Hello, world! Hello, world!'; + data = Uint8Array.from(Buffer.from(binaryData)); + otherData = Uint8Array.from(Buffer.from(binaryOtherData)); } else if (schema === ExternalPluginAdapterSchema.Json) { - data = JSON.stringify({ message: 'Hello', target: 'world' }); - otherData = JSON.stringify({ - message: 'Hello hello', - target: 'big wide world', - }); + const dataJson = { message: 'Hello', target: 'world' }; + const otherDataJson = { message: 'Hello hello', target: 'big wide world' }; + data = Uint8Array.from(Buffer.from(JSON.stringify(dataJson))); + otherData = Uint8Array.from(Buffer.from(JSON.stringify(otherDataJson))); + } else if (schema === ExternalPluginAdapterSchema.MsgPack) { + data = msgpack.encode({ message: 'Hello', target: 'msgpack' }); } if (!dataAuthoritySigner) { @@ -202,9 +207,9 @@ DATA_AUTHORITIES.forEach((dataAuthorityType) => { schema === ExternalPluginAdapterSchema.Binary || schema === ExternalPluginAdapterSchema.MsgPack ) { - assertData = Uint8Array.from(Buffer.from(data)); + assertData = data; } else if (schema === ExternalPluginAdapterSchema.Json) { - assertData = JSON.parse(data); + assertData = JSON.parse(Buffer.from(data).toString()); } // check the derived asset sdk correctly injects the data @@ -299,9 +304,9 @@ DATA_AUTHORITIES.forEach((dataAuthorityType) => { schema === ExternalPluginAdapterSchema.Binary || schema === ExternalPluginAdapterSchema.MsgPack ) { - assertData = Uint8Array.from(Buffer.from(data)); + assertData = data; } else if (schema === ExternalPluginAdapterSchema.Json) { - assertData = JSON.parse(data); + assertData = JSON.parse(Buffer.from(data).toString()); } // check the derived asset sdk correctly injects the data @@ -350,10 +355,13 @@ DATA_AUTHORITIES.forEach((dataAuthorityType) => { asset: asset.publicKey, }).sendAndConfirm(umi); - if (schema === ExternalPluginAdapterSchema.Binary) { - assertData = Uint8Array.from(Buffer.from(otherData)); + if ( + schema === ExternalPluginAdapterSchema.Binary || + schema === ExternalPluginAdapterSchema.MsgPack + ) { + assertData = otherData; } else if (schema === ExternalPluginAdapterSchema.Json) { - assertData = JSON.parse(otherData); + assertData = JSON.parse(Buffer.from(otherData).toString()); } // check the derived asset sdk correctly injects the data @@ -559,7 +567,7 @@ test(`updating a plugin before a secure app data does not corrupt the data`, asy ], }); - const assertData = Uint8Array.from(Buffer.from(data)); + const assertData = data; // check the derived asset sdk correctly injects the data await assertAsset( diff --git a/clients/rust/src/indexable_asset.rs b/clients/rust/src/indexable_asset.rs index 168572ee..5686ebd8 100644 --- a/clients/rust/src/indexable_asset.rs +++ b/clients/rust/src/indexable_asset.rs @@ -40,6 +40,40 @@ pub struct IndexableUnknownPluginSchemaV1 { pub data: String, } +#[cfg(feature = "serde")] +mod custom_serde { + use serde::{self, Deserialize, Deserializer, Serialize, Serializer}; + use serde_json::Value as JsonValue; + + pub fn serialize(data: &Option, serializer: S) -> Result + where + S: Serializer, + { + match data { + Some(s) => { + if let Ok(json_value) = serde_json::from_str::(s) { + json_value.serialize(serializer) + } else { + serializer.serialize_str(s) + } + } + None => serializer.serialize_none(), + } + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let json_value: Option = Option::deserialize(deserializer)?; + match json_value { + Some(JsonValue::String(s)) => Ok(Some(s)), + Some(json_value) => Ok(Some(json_value.to_string())), + None => Ok(None), + } + } +} + /// Schema used for indexing known external plugin types. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, Eq, PartialEq)] @@ -57,7 +91,10 @@ pub struct IndexableExternalPluginSchemaV1 { pub data_offset: Option, #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub data_len: Option, - #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + #[cfg_attr( + feature = "serde", + serde(skip_serializing_if = "Option::is_none", with = "custom_serde") + )] pub data: Option, } @@ -228,11 +265,13 @@ impl ProcessedExternalPlugin { let (data_offset, data_len, data) = match external_plugin_data_info { Some(data_info) => { let schema = match &adapter_config { - ExternalPluginAdapter::LifecycleHook(lifecycle_hook) => { - &lifecycle_hook.schema - } + ExternalPluginAdapter::LifecycleHook(lc_hook) => &lc_hook.schema, ExternalPluginAdapter::AppData(app_data) => &app_data.schema, - _ => &ExternalPluginAdapterSchema::Binary, // is this possible + ExternalPluginAdapter::LinkedLifecycleHook(l_lc_hook) => &l_lc_hook.schema, + ExternalPluginAdapter::LinkedAppData(l_app_data) => &l_app_data.schema, + ExternalPluginAdapter::DataSection(data_section) => &data_section.schema, + // Assume binary for `Oracle`, but this should never happen. + ExternalPluginAdapter::Oracle(_) => &ExternalPluginAdapterSchema::Binary, }; (