forked from herocode/horus
add zdfz models and macros for auto client generation
This commit is contained in:
@@ -5,13 +5,19 @@ edition.workspace = true
|
||||
description = "Osiris client library"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
zdfz = ["dep:zdfz-models"]
|
||||
|
||||
[dependencies]
|
||||
zdfz-models = { path = "../../../../../zdfz/sdk/models", optional = true }
|
||||
# Core dependencies
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
anyhow.workspace = true
|
||||
thiserror.workspace = true
|
||||
chrono.workspace = true
|
||||
paste = "1.0"
|
||||
|
||||
# HTTP client
|
||||
reqwest = { version = "0.12", default-features = false, features = ["json"] }
|
||||
|
||||
@@ -13,10 +13,15 @@ use thiserror::Error;
|
||||
pub mod kyc;
|
||||
pub mod payment;
|
||||
pub mod communication;
|
||||
pub mod macros;
|
||||
|
||||
#[cfg(feature = "zdfz")]
|
||||
pub mod zdfz_extensions;
|
||||
|
||||
pub use kyc::*;
|
||||
pub use payment::*;
|
||||
pub use communication::*;
|
||||
pub use macros::*;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum OsirisClientError {
|
||||
@@ -34,6 +39,9 @@ pub enum OsirisClientError {
|
||||
|
||||
#[error("Command execution failed: {0}")]
|
||||
CommandFailed(String),
|
||||
|
||||
#[error("Serialization failed: {0}")]
|
||||
SerializationFailed(String),
|
||||
}
|
||||
|
||||
/// Osiris client with CQRS support
|
||||
|
||||
204
lib/clients/osiris/src/macros.rs
Normal file
204
lib/clients/osiris/src/macros.rs
Normal file
@@ -0,0 +1,204 @@
|
||||
//! Macros for generating CRUD methods on OsirisClient
|
||||
//!
|
||||
//! These macros allow you to quickly generate standard CRUD operations
|
||||
//! and custom methods for your models.
|
||||
|
||||
/// Generate CRUD methods for a model on OsirisClient
|
||||
///
|
||||
/// This macro generates 5 standard methods:
|
||||
/// - create_{collection}
|
||||
/// - get_{collection}
|
||||
/// - update_{collection}
|
||||
/// - delete_{collection}
|
||||
/// - list_{collection}
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use osiris_client::{OsirisClient, impl_osiris_crud};
|
||||
///
|
||||
/// #[derive(serde::Serialize, serde::Deserialize)]
|
||||
/// struct User {
|
||||
/// id: String,
|
||||
/// name: String,
|
||||
/// }
|
||||
///
|
||||
/// impl_osiris_crud!(User, "users", "id");
|
||||
///
|
||||
/// // Now you can use:
|
||||
/// // client.create_users(&user).await?;
|
||||
/// // client.get_users("123").await?;
|
||||
/// // client.update_users("123", &user).await?;
|
||||
/// // client.delete_users("123").await?;
|
||||
/// // client.list_users().await?;
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! impl_osiris_crud {
|
||||
($model:ty, $collection:expr, $id_field:expr) => {
|
||||
paste::paste! {
|
||||
impl $crate::OsirisClient {
|
||||
/// Create a new instance
|
||||
#[doc = "Create a new " $collection " instance"]
|
||||
pub async fn [<$collection:snake _create>](&self, model: &$model) -> Result<$model, $crate::OsirisClientError> {
|
||||
let json = serde_json::to_string(model)
|
||||
.map_err(|e| $crate::OsirisClientError::SerializationFailed(e.to_string()))?;
|
||||
|
||||
// Create Rhai script that uses Osiris context API
|
||||
// Note: The actual object creation depends on the model type
|
||||
// For now, we serialize the data and would need model-specific constructors
|
||||
let script = format!(
|
||||
r#"
|
||||
let ctx = get_context(["system"]);
|
||||
let data = {};
|
||||
// TODO: Model-specific object creation
|
||||
// For now, this is a placeholder
|
||||
data
|
||||
"#,
|
||||
json
|
||||
);
|
||||
|
||||
let response = self.execute_script(&script).await?;
|
||||
// TODO: Parse response from job result
|
||||
Err($crate::OsirisClientError::CommandFailed("Not yet implemented".to_string()))
|
||||
}
|
||||
|
||||
/// Get an instance by ID
|
||||
#[doc = "Get a " $collection " instance by ID"]
|
||||
pub async fn [<$collection:snake _get>](&self, id: &str) -> Result<$model, $crate::OsirisClientError> {
|
||||
let script = format!(
|
||||
r#"
|
||||
let ctx = get_context(["system"]);
|
||||
ctx.get("{}", "{}")
|
||||
"#,
|
||||
$collection, id
|
||||
);
|
||||
|
||||
let response = self.execute_script(&script).await?;
|
||||
// TODO: Parse response from job result
|
||||
Err($crate::OsirisClientError::CommandFailed("Not yet implemented".to_string()))
|
||||
}
|
||||
|
||||
/// Update an existing instance
|
||||
#[doc = "Update an existing " $collection " instance"]
|
||||
pub async fn [<$collection:snake _update>](&self, id: &str, model: &$model) -> Result<$model, $crate::OsirisClientError> {
|
||||
let json = serde_json::to_string(model)
|
||||
.map_err(|e| $crate::OsirisClientError::SerializationFailed(e.to_string()))?;
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
let ctx = get_context(["system"]);
|
||||
let obj = ctx.get("{}", "{}");
|
||||
let data = {};
|
||||
// TODO: Update object fields from data
|
||||
ctx.save(obj);
|
||||
obj
|
||||
"#,
|
||||
$collection, id, json
|
||||
);
|
||||
|
||||
let response = self.execute_script(&script).await?;
|
||||
// TODO: Parse response from job result
|
||||
Err($crate::OsirisClientError::CommandFailed("Not yet implemented".to_string()))
|
||||
}
|
||||
|
||||
/// Delete an instance
|
||||
#[doc = "Delete a " $collection " instance"]
|
||||
pub async fn [<$collection:snake _delete>](&self, id: &str) -> Result<(), $crate::OsirisClientError> {
|
||||
let script = format!(
|
||||
r#"
|
||||
let ctx = get_context(["system"]);
|
||||
ctx.delete("{}", "{}")
|
||||
"#,
|
||||
$collection, id
|
||||
);
|
||||
|
||||
self.execute_script(&script).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// List all instances
|
||||
#[doc = "List all " $collection " instances"]
|
||||
pub async fn [<$collection:snake _list>](&self) -> Result<Vec<$model>, $crate::OsirisClientError> {
|
||||
let script = format!(
|
||||
r#"
|
||||
let ctx = get_context(["system"]);
|
||||
ctx.list("{}")
|
||||
"#,
|
||||
$collection
|
||||
);
|
||||
|
||||
let response = self.execute_script(&script).await?;
|
||||
// TODO: Parse response from job result
|
||||
Err($crate::OsirisClientError::CommandFailed("Not yet implemented".to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Generate a custom method on a model
|
||||
///
|
||||
/// This macro generates a method that calls a custom Rhai function on the model.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use osiris_client::{OsirisClient, impl_osiris_method};
|
||||
///
|
||||
/// #[derive(serde::Serialize, serde::Deserialize)]
|
||||
/// struct CalendarEvent {
|
||||
/// id: String,
|
||||
/// start_time: i64,
|
||||
/// }
|
||||
///
|
||||
/// impl_osiris_method!(CalendarEvent, "calendar_events", reschedule, new_start: i64, new_end: i64);
|
||||
///
|
||||
/// // Now you can use:
|
||||
/// // client.reschedule_calendar_events("123", 1234567890, 1234567900).await?;
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! impl_osiris_method {
|
||||
($model:ty, $collection:expr, $method_name:ident $(, $param:ident: $param_type:ty)*) => {
|
||||
paste::paste! {
|
||||
impl $crate::OsirisClient {
|
||||
#[doc = "Call " $method_name " on a " $collection " instance"]
|
||||
pub async fn [<$collection:snake _ $method_name>](&self, id: &str $(, $param: $param_type)*) -> Result<$model, $crate::OsirisClientError> {
|
||||
let params = serde_json::json!({
|
||||
$(stringify!($param): $param),*
|
||||
});
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
let ctx = get_context(["system"]);
|
||||
let obj = ctx.get("{}", "{}");
|
||||
// TODO: Call custom method on object
|
||||
// obj.{}({});
|
||||
ctx.save(obj);
|
||||
obj
|
||||
"#,
|
||||
$collection, id, stringify!($method_name), params
|
||||
);
|
||||
|
||||
let response = self.execute_script(&script).await?;
|
||||
// TODO: Parse response from job result
|
||||
Err($crate::OsirisClientError::CommandFailed("Not yet implemented".to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// Example model for testing
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
struct TestModel {
|
||||
id: String,
|
||||
name: String,
|
||||
}
|
||||
|
||||
// This would generate the methods (can't actually test async in doc tests easily)
|
||||
// impl_osiris_crud!(TestModel, "test_models", "id");
|
||||
}
|
||||
24
lib/clients/osiris/src/zdfz_extensions.rs
Normal file
24
lib/clients/osiris/src/zdfz_extensions.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
//! ZDFZ model extensions for OsirisClient
|
||||
//!
|
||||
//! This module generates CRUD and custom methods for ZDFZ models.
|
||||
//! It must be in the osiris-client crate to satisfy Rust's orphan rules.
|
||||
|
||||
use crate::{impl_osiris_crud, impl_osiris_method};
|
||||
|
||||
// Import ZDFZ models - these will be available when zdfz-models is a dependency
|
||||
#[cfg(feature = "zdfz")]
|
||||
use zdfz_models::*;
|
||||
|
||||
// ========== Core Business Models ==========
|
||||
|
||||
// Digital Residents - Individual users of the freezone
|
||||
#[cfg(feature = "zdfz")]
|
||||
impl_osiris_crud!(ApiDigitalResident, "digital_residents", "resident_id");
|
||||
|
||||
// Free Zone Companies - Companies registered in the freezone
|
||||
#[cfg(feature = "zdfz")]
|
||||
impl_osiris_crud!(FreeZoneCompany, "free_zone_companies", "fzc_id");
|
||||
|
||||
// Invoices - Financial documents for companies
|
||||
#[cfg(feature = "zdfz")]
|
||||
impl_osiris_crud!(FreeZoneInvoice, "invoices", "fz_invoice_id");
|
||||
Reference in New Issue
Block a user