commit c83fff8939521ded3f013ba3887433e31ce436cc
parent fcd644e80c3e7a94308b640c300271d1ca682e3a
Author: triesap <tyson@radroots.org>
Date: Mon, 13 Apr 2026 01:49:04 +0000
sdk: add sdk-first publish acceptance proofs
Diffstat:
4 files changed, 77 insertions(+), 17 deletions(-)
diff --git a/crates/sdk/src/adapters/radrootsd.rs b/crates/sdk/src/adapters/radrootsd.rs
@@ -1,6 +1,8 @@
use core::time::Duration;
+use crate::RadrootsNostrEvent;
use crate::config::RadrootsdAuth;
+use crate::listing;
use crate::listing::RadrootsListing;
use reqwest::header::{AUTHORIZATION, CONTENT_TYPE, HeaderMap, HeaderValue};
use serde::{Deserialize, Serialize};
@@ -26,6 +28,23 @@ pub struct SdkRadrootsdListingPublishRequest {
pub idempotency_key: Option<String>,
}
+impl SdkRadrootsdListingPublishRequest {
+ pub fn from_event(
+ event: &RadrootsNostrEvent,
+ signer_session_id: impl Into<String>,
+ signer_authority: Option<SdkRadrootsdSignerAuthority>,
+ idempotency_key: Option<String>,
+ ) -> Result<Self, listing::RadrootsTradeListingParseError> {
+ Ok(Self {
+ listing: listing::parse_event(event)?,
+ kind: Some(event.kind),
+ signer_session_id: signer_session_id.into(),
+ signer_authority,
+ idempotency_key,
+ })
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct SdkRadrootsdBridgePublishResponse {
pub deduplicated: bool,
@@ -108,10 +127,9 @@ pub async fn publish_listing(
request_builder = request_builder.header(CONTENT_TYPE, "application/json");
- let response = request_builder
- .send()
- .await
- .map_err(|err| RadrootsdError::Http(format!("send radrootsd listing publish request: {err}")))?;
+ let response = request_builder.send().await.map_err(|err| {
+ RadrootsdError::Http(format!("send radrootsd listing publish request: {err}"))
+ })?;
let status = response.status();
let body = response
.text()
diff --git a/crates/sdk/src/client.rs b/crates/sdk/src/client.rs
@@ -199,6 +199,14 @@ impl RadrootsSdkClient {
TradeClient { client: self }
}
+ #[cfg(any(
+ feature = "radrootsd-client",
+ all(
+ feature = "identity-models",
+ feature = "relay-client",
+ feature = "signing"
+ )
+ ))]
fn require_signer_mode(
&self,
required: SignerConfig,
@@ -391,6 +399,25 @@ impl<'a> ListingClient<'a> {
.await
}
+ #[cfg(all(
+ feature = "identity-models",
+ feature = "relay-client",
+ feature = "signing"
+ ))]
+ pub async fn publish_draft_with_identity(
+ &self,
+ identity: &RadrootsIdentity,
+ draft: WireEventParts,
+ ) -> Result<SdkPublishReceipt, SdkPublishError> {
+ self.client
+ .publish_parts_via_relay_with_identity(
+ identity,
+ draft,
+ "listing.publish_draft_with_identity",
+ )
+ .await
+ }
+
#[cfg(feature = "radrootsd-client")]
pub async fn publish_via_radrootsd(
&self,
diff --git a/crates/sdk/tests/radrootsd.rs b/crates/sdk/tests/radrootsd.rs
@@ -10,9 +10,9 @@ use radroots_sdk::listing::{
RadrootsListingProduct, RadrootsListingStatus,
};
use radroots_sdk::{
- RadrootsSdkClient, RadrootsSdkConfig, RadrootsdAuth, RadrootsdConfig, SdkEnvironment,
- SdkPublishError, SdkRadrootsdListingPublishRequest, SdkTransportMode, SdkTransportReceipt,
- SignerConfig,
+ RadrootsNostrEvent, RadrootsSdkClient, RadrootsSdkConfig, RadrootsdAuth, RadrootsdConfig,
+ SdkEnvironment, SdkPublishError, SdkRadrootsdListingPublishRequest, SdkTransportMode,
+ SdkTransportReceipt, SignerConfig, WireEventParts,
};
use serde_json::{Value, json};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
@@ -238,8 +238,20 @@ fn sample_listing() -> RadrootsListing {
}
}
+fn sdk_event(author: &str, created_at: u32, parts: WireEventParts) -> RadrootsNostrEvent {
+ RadrootsNostrEvent {
+ id: "event-1".to_owned(),
+ author: author.to_owned(),
+ created_at,
+ kind: parts.kind,
+ tags: parts.tags,
+ content: parts.content,
+ sig: "f".repeat(128),
+ }
+}
+
#[tokio::test]
-async fn radrootsd_listing_publish_returns_normalized_receipt() -> TestResult<()> {
+async fn radrootsd_listing_publish_accepts_sdk_built_draft() -> TestResult<()> {
let (server, request_rx) = JsonRpcServer::spawn(
Some("Bearer sdk-secret"),
json!({
@@ -274,13 +286,14 @@ async fn radrootsd_listing_publish_returns_normalized_receipt() -> TestResult<()
auth: RadrootsdAuth::BearerToken("sdk-secret".to_owned()),
};
let client = RadrootsSdkClient::from_config(config)?;
- let request = SdkRadrootsdListingPublishRequest {
- listing: sample_listing(),
- kind: None,
- signer_session_id: "session-123".to_owned(),
- signer_authority: None,
- idempotency_key: Some("idem-1".to_owned()),
- };
+ let draft = client.listing().build_draft(&sample_listing())?;
+ let event = sdk_event("seller", 1_720_000_000, draft);
+ let request = SdkRadrootsdListingPublishRequest::from_event(
+ &event,
+ "session-123",
+ None,
+ Some("idem-1".to_owned()),
+ )?;
let receipt = client.listing().publish_via_radrootsd(&request).await?;
let request_json = request_rx.await?;
@@ -288,6 +301,7 @@ async fn radrootsd_listing_publish_returns_normalized_receipt() -> TestResult<()
assert_eq!(request_json["method"], "bridge.listing.publish");
assert_eq!(request_json["params"]["signer_session_id"], "session-123");
assert_eq!(request_json["params"]["idempotency_key"], "idem-1");
+ assert_eq!(request_json["params"]["kind"], 30402);
assert_eq!(
request_json["params"]["listing"]["d_tag"],
"AAAAAAAAAAAAAAAAAAAAAg"
diff --git a/crates/sdk/tests/relay_direct.rs b/crates/sdk/tests/relay_direct.rs
@@ -161,7 +161,7 @@ fn sample_listing() -> RadrootsListing {
}
#[tokio::test]
-async fn relay_direct_listing_publish_returns_normalized_receipt() -> TestResult<()> {
+async fn relay_direct_listing_publish_accepts_sdk_built_draft() -> TestResult<()> {
let relay = AckRelay::spawn().await?;
let identity = RadrootsIdentity::generate();
let mut config = RadrootsSdkConfig::for_environment(SdkEnvironment::Custom);
@@ -175,10 +175,11 @@ async fn relay_direct_listing_publish_returns_normalized_receipt() -> TestResult
auth: RadrootsdAuth::None,
};
let client = RadrootsSdkClient::from_config(config)?;
+ let draft = client.listing().build_draft(&sample_listing())?;
let receipt = client
.listing()
- .publish_with_identity(&identity, &sample_listing())
+ .publish_draft_with_identity(&identity, draft)
.await?;
assert_eq!(receipt.transport, SdkTransportMode::RelayDirect);