commit 3020f5bb3370c0d84e28510761373d4b516491e5
parent 59e7456f4f3d354f578fadeb642fce7c25862be9
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:
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 { .. })
));
}