Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions sdk/rust/src/api/contacts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//! First-level mutual contact graph (`/contacts`). Mirrors
//! `sdk/typescript/src/api/contacts.ts`.
//!
//! An **accepted** contact relationship is the prerequisite for direct
//! messaging — the relay refuses a DM between two agents that are not contacts
//! (`not_a_contact`). Typical bootstrap:
//!
//! ```ignore
//! client.contacts.request("@peer").await?; // initiator
//! client.contacts.accept("@initiator").await?; // peer (or auto-accept on a
//! // reverse-pending request)
//! client.messages.send(envelope).await?; // now permitted
//! ```
//!
//! All calls are authenticated as the acting agent (`X-Agent-ID` + signature).
//! Contacts are keyed by cryptoId. Responses are returned as [`serde_json::Value`]
//! (the backend contact record shape is not needed by current callers).

use crate::error::Result;
use crate::http::HttpClient;
use crate::util::encode;

/// Manages the mutual first-level contact graph: send/accept/decline requests,
/// block, and list contacts.
#[derive(Clone)]
pub struct ContactsApi {
http: HttpClient,
}

impl ContactsApi {
pub(crate) fn new(http: HttpClient) -> Self {
Self { http }
}

/// Send a contact request to `agent_id` (idempotent; **auto-accepts** when a
/// reverse request from `agent_id` is already pending).
pub async fn request(&self, agent_id: &str) -> Result<serde_json::Value> {
self.http
.post_agent_auth::<serde_json::Value, serde_json::Value>(
&format!("/contacts/{}", encode(agent_id)),
None,
)
.await
}

/// Accept a pending incoming request from `agent_id`.
pub async fn accept(&self, agent_id: &str) -> Result<serde_json::Value> {
self.http
.post_agent_auth::<serde_json::Value, serde_json::Value>(
&format!("/contacts/{}/accept", encode(agent_id)),
None,
)
.await
}

/// Decline an incoming request, cancel an outgoing one, or remove a contact.
pub async fn remove(&self, agent_id: &str) -> Result<()> {
self.http
.delete_agent_auth::<(), serde_json::Value>(
&format!("/contacts/{}", encode(agent_id)),
None,
)
.await
}

/// Block `agent_id`, suppressing the relationship and refusing new requests.
pub async fn block(&self, agent_id: &str) -> Result<serde_json::Value> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add the unblock wrapper alongside block

When a Rust client uses this new block() method, the SDK has no corresponding wrapper for POST /contacts/{id}/unblock, even though the mirrored TypeScript API exposes unblock() at sdk/typescript/src/api/contacts.ts:71. In the blocked-contact scenario, Rust callers cannot restore the relationship through client.contacts and must drop to raw client.http(), making this newly added contact graph surface one-way for users who need to resume messaging.

Useful? React with 👍 / 👎.

self.http
.post_agent_auth::<serde_json::Value, serde_json::Value>(
&format!("/contacts/{}/block", encode(agent_id)),
None,
)
.await
}

/// Get the relationship status with `agent_id`.
pub async fn status(&self, agent_id: &str) -> Result<serde_json::Value> {
self.http
.get_agent_auth(&format!("/contacts/{}/status", encode(agent_id)), &[])
.await
}

/// List the acting agent's accepted contacts.
pub async fn list(&self) -> Result<serde_json::Value> {
self.http.get_agent_auth("/contacts", &[]).await
}

/// List pending incoming and outgoing requests.
pub async fn requests(&self) -> Result<serde_json::Value> {
self.http.get_agent_auth("/contacts/requests", &[]).await
}
}
1 change: 1 addition & 0 deletions sdk/rust/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod admin;
pub mod bounties;
pub mod broadcasts;
pub mod channels;
pub mod contacts;
pub mod conversations;
pub mod directory;
pub mod docs;
Expand Down
3 changes: 3 additions & 0 deletions sdk/rust/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crate::api::admin::AdminApi;
use crate::api::bounties::BountiesApi;
use crate::api::broadcasts::BroadcastsApi;
use crate::api::channels::ChannelsApi;
use crate::api::contacts::ContactsApi;
use crate::api::conversations::ConversationsApi;
use crate::api::directory::DirectoryApi;
use crate::api::docs::DocsApi;
Expand Down Expand Up @@ -86,6 +87,7 @@ pub struct TinyPlaceClient {
pub reputation: ReputationApi,
pub inbox: InboxApi,
pub channels: ChannelsApi,
pub contacts: ContactsApi,
pub conversations: ConversationsApi,
pub broadcasts: BroadcastsApi,
pub bounties: BountiesApi,
Expand Down Expand Up @@ -133,6 +135,7 @@ impl TinyPlaceClient {
reputation: ReputationApi::new(http.clone()),
inbox: InboxApi::new(http.clone()),
channels: ChannelsApi::new(http.clone()),
contacts: ContactsApi::new(http.clone()),
conversations: ConversationsApi::new(http.clone()),
broadcasts: BroadcastsApi::new(http.clone()),
bounties: BountiesApi::new(http.clone()),
Expand Down
Loading