lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

commit 6b38872141aa99e7bfe20b42e6473084abadead0
parent 9dbd53dc24b8aff3d421a6cac4c5cbec0616d7da
Author: triesap <tyson@radroots.org>
Date:   Fri, 26 Jun 2026 13:15:30 +0000

Add SDK farm private location clearing

Add the farm private location clear request/receipt path, private-store deletion support, and unit coverage for missing, unauthorized, invalid, stored, and closed-store clear behavior.

Diffstat:
Mcrates/sdk/src/farms_runtime.rs | 40++++++++++++++++++++++++++++++++++++++++
Mcrates/sdk/src/lib.rs | 5+++--
Mcrates/sdk/src/private_store.rs | 17+++++++++++++++++
Mcrates/sdk/tests/unit/farms_runtime_tests.rs | 69++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
4 files changed, 124 insertions(+), 7 deletions(-)

diff --git a/crates/sdk/src/farms_runtime.rs b/crates/sdk/src/farms_runtime.rs @@ -224,6 +224,32 @@ impl FarmPrivateLocationUpsertRequest { } #[cfg(feature = "runtime")] +#[derive(Clone, Debug, serde::Serialize)] +#[non_exhaustive] +pub struct FarmPrivateLocationClearRequest { + #[serde(serialize_with = "crate::actor_json::serialize_actor_context")] + pub actor: RadrootsActorContext, + pub farm_d_tag: String, +} + +#[cfg(feature = "runtime")] +impl FarmPrivateLocationClearRequest { + pub fn new(actor: RadrootsActorContext, farm_d_tag: impl Into<String>) -> Self { + Self { + actor, + farm_d_tag: farm_d_tag.into(), + } + } +} + +#[cfg(feature = "runtime")] +#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct FarmPrivateLocationClearReceipt { + pub farm_addr: RadrootsAddressableCoordinate, + pub cleared: bool, +} + +#[cfg(feature = "runtime")] #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] pub struct FarmPrivateLocationReceipt { pub farm_addr: RadrootsAddressableCoordinate, @@ -399,6 +425,20 @@ impl<'sdk> FarmsClient<'sdk> { .transpose() } + pub async fn clear_private_location( + &self, + request: FarmPrivateLocationClearRequest, + ) -> Result<FarmPrivateLocationClearReceipt, RadrootsSdkError> { + require_farmer_actor(&request.actor, FARM_PRIVATE_LOCATION_OPERATION)?; + let farm_addr = farm_addr(&request.actor, request.farm_d_tag.as_str())?; + let cleared = self + .sdk + ._private_store + .delete_farm_location(&farm_addr) + .await?; + Ok(FarmPrivateLocationClearReceipt { farm_addr, cleared }) + } + fn resolved_created_at( &self, created_at: Option<RadrootsSdkTimestamp>, diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs @@ -69,8 +69,9 @@ pub use crate::error::{ #[cfg(feature = "runtime")] pub use crate::farms_runtime::{ FARM_PUBLISH_OPERATION_KIND, FarmEnqueuePublishRequest, FarmEnqueueReceipt, - FarmPreparePublishRequest, FarmPrivateLocationReceipt, FarmPrivateLocationUpsertRequest, - FarmPublishPlan, SdkExactLocation, SdkPublicLocality, + FarmPreparePublishRequest, FarmPrivateLocationClearReceipt, FarmPrivateLocationClearRequest, + FarmPrivateLocationReceipt, FarmPrivateLocationUpsertRequest, FarmPublishPlan, + SdkExactLocation, SdkPublicLocality, }; #[cfg(feature = "runtime")] pub use crate::geonames::{ diff --git a/crates/sdk/src/private_store.rs b/crates/sdk/src/private_store.rs @@ -197,6 +197,23 @@ impl SdkPrivateStore { row.map(|row| private_farm_location_from_row(farm_addr.clone(), row)) .transpose() } + + pub async fn delete_farm_location( + &self, + farm_addr: &RadrootsAddressableCoordinate, + ) -> Result<bool, RadrootsSdkError> { + sqlx::query( + r#" + DELETE FROM sdk_private_farm_location + WHERE farm_addr = ?1 + "#, + ) + .bind(farm_addr.as_str()) + .execute(&self.pool) + .await + .map(|receipt| receipt.rows_affected() > 0) + .map_err(private_store_error) + } } async fn configure_connection( diff --git a/crates/sdk/tests/unit/farms_runtime_tests.rs b/crates/sdk/tests/unit/farms_runtime_tests.rs @@ -546,10 +546,10 @@ async fn farm_private_location_default_client_and_lookup_report_store_edges() { }) )); - let farm_addr = farm_addr(&actor, FARM_A_D_TAG).expect("farm addr"); + let farm_a_addr = farm_addr(&actor, FARM_A_D_TAG).expect("farm addr"); assert_eq!( sdk.farms() - .private_location(&farm_addr) + .private_location(&farm_a_addr) .await .expect("missing location"), None @@ -568,11 +568,11 @@ async fn farm_private_location_default_client_and_lookup_report_store_edges() { ) .await .expect("stored location"); - assert_eq!(stored.farm_addr, farm_addr); + assert_eq!(stored.farm_addr, farm_a_addr); assert_eq!(stored.public_locality.primary, "Fixture Town"); assert_eq!( sdk.farms() - .private_location(&farm_addr) + .private_location(&farm_a_addr) .await .expect("stored lookup") .expect("stored location") @@ -580,6 +580,59 @@ async fn farm_private_location_default_client_and_lookup_report_store_edges() { 1_700_000_501_000 ); + let missing_clear = sdk + .farms() + .clear_private_location(FarmPrivateLocationClearRequest::new( + actor.clone(), + FARM_B_D_TAG, + )) + .await + .expect("clear missing location"); + assert!(!missing_clear.cleared); + assert_eq!( + missing_clear.farm_addr, + farm_addr(&actor, FARM_B_D_TAG).expect("farm b addr") + ); + + let non_farmer_actor = + RadrootsActorContext::test(FARMER, [RadrootsActorRole::Buyer]).expect("buyer actor"); + assert!(matches!( + sdk.farms() + .clear_private_location(FarmPrivateLocationClearRequest::new( + non_farmer_actor, + FARM_A_D_TAG, + )) + .await, + Err(RadrootsSdkError::UnauthorizedActor { .. }) + )); + assert!(matches!( + sdk.farms() + .clear_private_location(FarmPrivateLocationClearRequest::new( + actor.clone(), + "bad d tag", + )) + .await, + Err(RadrootsSdkError::InvalidRequest { .. }) + )); + + let cleared = sdk + .farms() + .clear_private_location(FarmPrivateLocationClearRequest::new( + actor.clone(), + FARM_A_D_TAG, + )) + .await + .expect("clear stored location"); + assert!(cleared.cleared); + assert_eq!(cleared.farm_addr, farm_a_addr); + assert_eq!( + sdk.farms() + .private_location(&farm_a_addr) + .await + .expect("location cleared"), + None + ); + assert!(matches!( sdk.farms() .upsert_private_location_with_geocoder( @@ -674,7 +727,13 @@ async fn farm_private_location_default_client_and_lookup_report_store_edges() { Err(RadrootsSdkError::PrivateStore { .. }) )); assert!(matches!( - sdk.farms().private_location(&farm_addr).await, + sdk.farms().private_location(&farm_a_addr).await, + Err(RadrootsSdkError::PrivateStore { .. }) + )); + assert!(matches!( + sdk.farms() + .clear_private_location(FarmPrivateLocationClearRequest::new(actor, FARM_A_D_TAG)) + .await, Err(RadrootsSdkError::PrivateStore { .. }) )); }