AkurAI Build
Menu

Public repository / master

AkurAI-Drive / contracts/src/lib.rs

Latest change 54e252bb8ef81aab2ef9033bff0874b8afe40922 - refactor(drive): share versioned API contracts by AkurAI Agent

//! Versioned JSON wire types shared by the Drive API and native clients.
//!
//! `created` is always Unix seconds, matching the SQLite storage schema.

use serde::{Deserialize, Serialize};

pub const API_VERSION: &str = "v1";

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Bucket {
    pub bucket: String,
    #[serde(default)]
    pub region: String,
    #[serde(default)]
    pub plan: String,
    #[serde(default)]
    pub org: String,
    #[serde(default)]
    pub created: i64,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct StorageBucket {
    pub id: String,
    pub org: String,
    pub plan: String,
    pub bucket: String,
    pub region: String,
    pub created: i64,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct RemoteFile {
    pub key: String,
    #[serde(default)]
    pub size: u64,
    #[serde(default)]
    pub etag: String,
    #[serde(default)]
    pub last_modified: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Presign {
    pub url: String,
    pub method: String,
    #[serde(default)]
    pub expires_in: u64,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct Identity {
    #[serde(default)]
    pub authenticated: bool,
    #[serde(default)]
    pub sub: String,
    #[serde(default)]
    pub email: String,
    #[serde(default)]
    pub groups: Vec<String>,
    #[serde(default)]
    pub is_admin: bool,
    #[serde(default)]
    pub tenant_id: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct BucketsResponse {
    pub buckets: Vec<Bucket>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct StorageResponse {
    pub buckets: Vec<StorageBucket>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct FilesResponse {
    pub files: Vec<RemoteFile>,
}

#[cfg(test)]
mod tests {
    use super::{Bucket, BucketsResponse};

    #[test]
    fn bucket_fixture_round_trips_unix_seconds() {
        let fixture = r#"{"buckets":[{"bucket":"drive-acme","region":"eu-west-1","plan":"pro","org":"Acme ehf.","created":1700000000}]}"#;
        let response: BucketsResponse = serde_json::from_str(fixture).expect("bucket fixture");
        assert_eq!(response.buckets[0].created, 1_700_000_000);
        assert_eq!(
            serde_json::to_value(&response.buckets[0]).unwrap()["created"],
            1_700_000_000
        );
    }

    #[test]
    fn bucket_defaults_missing_optional_fields() {
        let bucket: Bucket = serde_json::from_str(r#"{"bucket":"drive-acme"}"#).unwrap();
        assert_eq!(bucket.created, 0);
        assert!(bucket.region.is_empty());
    }
}