this/core/
service.rs

1//! Service traits for data and link operations
2
3use crate::core::{Data, EntityReference, Link};
4use anyhow::Result;
5use async_trait::async_trait;
6use uuid::Uuid;
7
8/// Service trait for managing data entities
9///
10/// Implementations provide CRUD operations for a specific entity type.
11/// The framework is agnostic to the underlying storage mechanism.
12#[async_trait]
13pub trait DataService<T: Data>: Send + Sync {
14    /// Create a new entity
15    async fn create(&self, tenant_id: &Uuid, entity: T) -> Result<T>;
16
17    /// Get an entity by ID
18    async fn get(&self, tenant_id: &Uuid, id: &Uuid) -> Result<Option<T>>;
19
20    /// List all entities for a tenant
21    async fn list(&self, tenant_id: &Uuid) -> Result<Vec<T>>;
22
23    /// Update an existing entity
24    async fn update(&self, tenant_id: &Uuid, id: &Uuid, entity: T) -> Result<T>;
25
26    /// Delete an entity
27    async fn delete(&self, tenant_id: &Uuid, id: &Uuid) -> Result<()>;
28
29    /// Search entities by field values
30    async fn search(&self, tenant_id: &Uuid, field: &str, value: &str) -> Result<Vec<T>>;
31}
32
33/// Service trait for managing links between entities
34///
35/// This service is completely agnostic to entity types - it only knows
36/// about EntityReferences and link types (both Strings).
37#[async_trait]
38pub trait LinkService: Send + Sync {
39    /// Create a new link between two entities
40    async fn create(
41        &self,
42        tenant_id: &Uuid,
43        link_type: &str,
44        source: EntityReference,
45        target: EntityReference,
46        metadata: Option<serde_json::Value>,
47    ) -> Result<Link>;
48
49    /// Get a specific link by ID
50    async fn get(&self, tenant_id: &Uuid, id: &Uuid) -> Result<Option<Link>>;
51
52    /// List all links for a tenant
53    async fn list(&self, tenant_id: &Uuid) -> Result<Vec<Link>>;
54
55    /// Find links by source entity
56    ///
57    /// Optionally filter by link_type and/or target_type
58    async fn find_by_source(
59        &self,
60        tenant_id: &Uuid,
61        source_id: &Uuid,
62        source_type: &str,
63        link_type: Option<&str>,
64        target_type: Option<&str>,
65    ) -> Result<Vec<Link>>;
66
67    /// Find links by target entity
68    ///
69    /// Optionally filter by link_type and/or source_type
70    async fn find_by_target(
71        &self,
72        tenant_id: &Uuid,
73        target_id: &Uuid,
74        target_type: &str,
75        link_type: Option<&str>,
76        source_type: Option<&str>,
77    ) -> Result<Vec<Link>>;
78
79    /// Update a link's metadata
80    ///
81    /// This allows updating the metadata associated with a link without
82    /// recreating it. Useful for adding/modifying contextual information
83    /// like status, dates, permissions, etc.
84    async fn update(
85        &self,
86        tenant_id: &Uuid,
87        id: &Uuid,
88        metadata: Option<serde_json::Value>,
89    ) -> Result<Link>;
90
91    /// Delete a link
92    async fn delete(&self, tenant_id: &Uuid, id: &Uuid) -> Result<()>;
93
94    /// Delete all links involving a specific entity
95    ///
96    /// Used when deleting an entity to maintain referential integrity
97    async fn delete_by_entity(
98        &self,
99        tenant_id: &Uuid,
100        entity_id: &Uuid,
101        entity_type: &str,
102    ) -> Result<()>;
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108    use crate::core::entity::Entity;
109
110    // Mock entity for testing
111    #[derive(Clone, Debug)]
112    struct TestEntity {
113        id: Uuid,
114        tenant_id: Uuid,
115    }
116
117    impl Entity for TestEntity {
118        type Service = ();
119        fn resource_name() -> &'static str {
120            "tests"
121        }
122        fn resource_name_singular() -> &'static str {
123            "test"
124        }
125        fn service_from_host(
126            _: &std::sync::Arc<dyn std::any::Any + Send + Sync>,
127        ) -> Result<std::sync::Arc<Self::Service>> {
128            Ok(std::sync::Arc::new(()))
129        }
130    }
131
132    impl Data for TestEntity {
133        fn id(&self) -> Uuid {
134            self.id
135        }
136        fn tenant_id(&self) -> Uuid {
137            self.tenant_id
138        }
139        fn indexed_fields() -> &'static [&'static str] {
140            &[]
141        }
142        fn field_value(&self, _field: &str) -> Option<crate::core::field::FieldValue> {
143            None
144        }
145    }
146
147    // The traits compile and can be used in generic contexts
148    async fn generic_create<T, S>(service: &S, tenant_id: &Uuid, entity: T) -> Result<T>
149    where
150        T: Data,
151        S: DataService<T>,
152    {
153        service.create(tenant_id, entity).await
154    }
155
156    #[test]
157    fn test_traits_compile() {
158        // This test just verifies that the traits are correctly defined
159        // and can be used in generic contexts
160    }
161}