Start CQRS refactoring: Create Osiris client crate
- Added workspace structure to Osiris Cargo.toml - Created osiris-client crate for query operations (GET requests) - Implemented generic get(), list(), query() methods - Added KYC, payment, and communication query modules - Created comprehensive refactoring plan document CQRS Pattern: - Commands (writes) → Supervisor client → Rhai scripts - Queries (reads) → Osiris client → REST API Next steps: - Implement Osiris server with Axum - Restructure SDK client by category (kyc/, payment/, etc.) - Update FreezoneClient to use both supervisor and osiris clients
This commit is contained in:
14
client/Cargo.toml
Normal file
14
client/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "osiris-client"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
anyhow = "1.0"
|
||||
thiserror = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.23", features = ["full", "macros"] }
|
||||
37
client/src/communication.rs
Normal file
37
client/src/communication.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
//! Communication query methods (email verification, etc.)
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::{OsirisClient, OsirisClientError};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Verification {
|
||||
pub id: String,
|
||||
pub email: String,
|
||||
pub code: String,
|
||||
pub transport: String,
|
||||
pub status: VerificationStatus,
|
||||
pub created_at: i64,
|
||||
pub expires_at: i64,
|
||||
pub verified_at: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum VerificationStatus {
|
||||
Pending,
|
||||
Verified,
|
||||
Expired,
|
||||
Failed,
|
||||
}
|
||||
|
||||
impl OsirisClient {
|
||||
/// Get verification by ID
|
||||
pub async fn get_verification(&self, verification_id: &str) -> Result<Verification, OsirisClientError> {
|
||||
self.get("verification", verification_id).await
|
||||
}
|
||||
|
||||
/// Get verification by email
|
||||
pub async fn get_verification_by_email(&self, email: &str) -> Result<Vec<Verification>, OsirisClientError> {
|
||||
self.query("verification", &format!("email={}", email)).await
|
||||
}
|
||||
}
|
||||
38
client/src/kyc.rs
Normal file
38
client/src/kyc.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
//! KYC query methods
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::{OsirisClient, OsirisClientError};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct KycSession {
|
||||
pub id: String,
|
||||
pub resident_id: String,
|
||||
pub status: KycSessionStatus,
|
||||
pub kyc_url: Option<String>,
|
||||
pub created_at: i64,
|
||||
pub updated_at: i64,
|
||||
pub expires_at: i64,
|
||||
pub verified_at: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum KycSessionStatus {
|
||||
Pending,
|
||||
InProgress,
|
||||
Completed,
|
||||
Failed,
|
||||
Expired,
|
||||
}
|
||||
|
||||
impl OsirisClient {
|
||||
/// Get KYC session by ID
|
||||
pub async fn get_kyc_session(&self, session_id: &str) -> Result<KycSession, OsirisClientError> {
|
||||
self.get("kyc_session", session_id).await
|
||||
}
|
||||
|
||||
/// List all KYC sessions for a resident
|
||||
pub async fn list_kyc_sessions_by_resident(&self, resident_id: &str) -> Result<Vec<KycSession>, OsirisClientError> {
|
||||
self.query("kyc_session", &format!("resident_id={}", resident_id)).await
|
||||
}
|
||||
}
|
||||
119
client/src/lib.rs
Normal file
119
client/src/lib.rs
Normal file
@@ -0,0 +1,119 @@
|
||||
//! Osiris Client - Query API for Osiris data structures
|
||||
//!
|
||||
//! This client provides read-only access to Osiris data via REST API.
|
||||
//! Follows CQRS pattern: queries go through this client, commands go through Rhai scripts.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
pub mod kyc;
|
||||
pub mod payment;
|
||||
pub mod communication;
|
||||
|
||||
pub use kyc::*;
|
||||
pub use payment::*;
|
||||
pub use communication::*;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum OsirisClientError {
|
||||
#[error("HTTP request failed: {0}")]
|
||||
RequestFailed(#[from] reqwest::Error),
|
||||
|
||||
#[error("Resource not found: {0}")]
|
||||
NotFound(String),
|
||||
|
||||
#[error("Deserialization failed: {0}")]
|
||||
DeserializationFailed(String),
|
||||
}
|
||||
|
||||
/// Osiris client for querying data
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OsirisClient {
|
||||
base_url: String,
|
||||
client: reqwest::Client,
|
||||
}
|
||||
|
||||
impl OsirisClient {
|
||||
/// Create a new Osiris client
|
||||
pub fn new(base_url: impl Into<String>) -> Self {
|
||||
Self {
|
||||
base_url: base_url.into(),
|
||||
client: reqwest::Client::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic GET request for any struct by ID
|
||||
pub async fn get<T>(&self, struct_name: &str, id: &str) -> Result<T, OsirisClientError>
|
||||
where
|
||||
T: for<'de> Deserialize<'de>,
|
||||
{
|
||||
let url = format!("{}/api/{}/{}", self.base_url, struct_name, id);
|
||||
|
||||
let response = self.client
|
||||
.get(&url)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if response.status() == 404 {
|
||||
return Err(OsirisClientError::NotFound(format!("{}/{}", struct_name, id)));
|
||||
}
|
||||
|
||||
let data = response
|
||||
.json::<T>()
|
||||
.await
|
||||
.map_err(|e| OsirisClientError::DeserializationFailed(e.to_string()))?;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
/// Generic LIST request for all instances of a struct
|
||||
pub async fn list<T>(&self, struct_name: &str) -> Result<Vec<T>, OsirisClientError>
|
||||
where
|
||||
T: for<'de> Deserialize<'de>,
|
||||
{
|
||||
let url = format!("{}/api/{}", self.base_url, struct_name);
|
||||
|
||||
let response = self.client
|
||||
.get(&url)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
let data = response
|
||||
.json::<Vec<T>>()
|
||||
.await
|
||||
.map_err(|e| OsirisClientError::DeserializationFailed(e.to_string()))?;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
/// Generic QUERY request with filters
|
||||
pub async fn query<T>(&self, struct_name: &str, query: &str) -> Result<Vec<T>, OsirisClientError>
|
||||
where
|
||||
T: for<'de> Deserialize<'de>,
|
||||
{
|
||||
let url = format!("{}/api/{}?{}", self.base_url, struct_name, query);
|
||||
|
||||
let response = self.client
|
||||
.get(&url)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
let data = response
|
||||
.json::<Vec<T>>()
|
||||
.await
|
||||
.map_err(|e| OsirisClientError::DeserializationFailed(e.to_string()))?;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_client_creation() {
|
||||
let client = OsirisClient::new("http://localhost:8080");
|
||||
assert_eq!(client.base_url, "http://localhost:8080");
|
||||
}
|
||||
}
|
||||
39
client/src/payment.rs
Normal file
39
client/src/payment.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
//! Payment query methods
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::{OsirisClient, OsirisClientError};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Payment {
|
||||
pub id: String,
|
||||
pub amount: f64,
|
||||
pub currency: String,
|
||||
pub status: PaymentStatus,
|
||||
pub description: String,
|
||||
pub payment_url: Option<String>,
|
||||
pub created_at: i64,
|
||||
pub updated_at: i64,
|
||||
pub completed_at: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum PaymentStatus {
|
||||
Pending,
|
||||
Processing,
|
||||
Completed,
|
||||
Failed,
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
impl OsirisClient {
|
||||
/// Get payment by ID
|
||||
pub async fn get_payment(&self, payment_id: &str) -> Result<Payment, OsirisClientError> {
|
||||
self.get("payment", payment_id).await
|
||||
}
|
||||
|
||||
/// List all payments
|
||||
pub async fn list_payments(&self) -> Result<Vec<Payment>, OsirisClientError> {
|
||||
self.list("payment").await
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user