areev 0.2.0

Rust SDK for the Areev knowledge database — gRPC and HTTP transports
Documentation
//! `Hooks` resource — outbound event webhooks on memory mutations.
//!
//! Mirrors the Python SDK's `client.hooks.*` surface.
//!
//! Requires: Scale or Custom plan. The server returns `FTR-E001`
//! (surfaced as [`crate::AreevError::FeatureNotAvailable`]) when the
//! org's tier does not include hooks — the SDK never gates client-side.

use serde_json::{Map, Value};

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

/// Event-hook lifecycle.
///
/// Access via [`crate::Areev::hooks`].
pub struct Hooks<'a> {
    http: &'a HttpClient,
    memory_id: String,
}

impl<'a> Hooks<'a> {
    /// Internal constructor — use [`crate::Areev::hooks`].
    pub(crate) fn new(http: &'a HttpClient, memory_id: String) -> Self {
        Self { http, memory_id }
    }

    /// Start a [`CreateHookBuilder`] for a new event hook.
    ///
    /// When `generate_secret` is set the response carries a one-time
    /// signing secret — shown once, never retrievable; do not log it.
    /// Requires: Scale or Custom plan. Emits an audit event.
    pub fn create<'b>(&'b self, name: &str) -> CreateHookBuilder<'b, 'a> {
        CreateHookBuilder::new(self, name)
    }

    /// List configured hooks. Requires: Scale or Custom plan.
    pub async fn list(&self) -> Result<Value> {
        let path = format!("/memories/{}/hooks", self.memory_id);
        self.http._get(&path, None).await
    }

    /// Hook delivery statistics. Requires: Scale or Custom plan.
    pub async fn stats(&self) -> Result<Value> {
        let path = format!("/memories/{}/hooks/stats", self.memory_id);
        self.http._get(&path, None).await
    }

    /// Delete a hook. Requires: Scale or Custom plan. Emits an audit
    /// event.
    pub async fn delete(&self, hook_id: &str) -> Result<()> {
        let path = format!("/memories/{}/hooks/{}", self.memory_id, hook_id);
        self.http._delete(&path).await.map(|_| ())
    }
}

/// Builder for [`Hooks::create`].
pub struct CreateHookBuilder<'b, 'a> {
    hooks: &'b Hooks<'a>,
    body: Map<String, Value>,
}

impl<'b, 'a> CreateHookBuilder<'b, 'a> {
    fn new(hooks: &'b Hooks<'a>, name: &str) -> Self {
        let mut body = Map::new();
        body.insert("name".into(), Value::String(name.to_string()));
        Self { hooks, body }
    }

    /// Delivery URL the hook POSTs to.
    pub fn url(mut self, url: &str) -> Self {
        self.body
            .insert("url".into(), Value::String(url.to_string()));
        self
    }

    /// Event types to fire on (e.g. `grain.added`, `grain.forgotten`).
    pub fn event_filters(mut self, filters: &[&str]) -> Self {
        self.body.insert(
            "event_filters".into(),
            Value::Array(
                filters
                    .iter()
                    .map(|f| Value::String(f.to_string()))
                    .collect(),
            ),
        );
        self
    }

    /// Delivery mode — e.g. `sync`, `async`.
    pub fn delivery_mode(mut self, mode: &str) -> Self {
        self.body
            .insert("delivery_mode".into(), Value::String(mode.to_string()));
        self
    }

    /// Redaction mode applied to the payload before delivery.
    pub fn redaction_mode(mut self, mode: &str) -> Self {
        self.body
            .insert("redaction_mode".into(), Value::String(mode.to_string()));
        self
    }

    /// Request a one-time signing secret in the response.
    pub fn generate_secret(mut self, generate: bool) -> Self {
        self.body
            .insert("generate_secret".into(), Value::Bool(generate));
        self
    }

    /// Acknowledge that an un-redacted hook may deliver PII off-platform.
    pub fn acknowledge_pii_risk(mut self, ack: bool) -> Self {
        self.body
            .insert("acknowledge_pii_risk".into(), Value::Bool(ack));
        self
    }

    /// Issue the create request.
    pub async fn send(self) -> Result<Value> {
        let path = format!("/memories/{}/hooks", self.hooks.memory_id);
        self.hooks
            .http
            ._post(&path, Some(&Value::Object(self.body)))
            .await
    }
}