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    #[allow(dead_code)]
112    #[derive(Clone, Debug)]
113    struct TestEntity {
114        id: Uuid,
115        tenant_id: Uuid,
116    }
117
118    impl Entity for TestEntity {
119        type Service = ();
120        fn resource_name() -> &'static str {
121            "tests"
122        }
123        fn resource_name_singular() -> &'static str {
124            "test"
125        }
126        fn service_from_host(
127            _: &std::sync::Arc<dyn std::any::Any + Send + Sync>,
128        ) -> Result<std::sync::Arc<Self::Service>> {
129            Ok(std::sync::Arc::new(()))
130        }
131    }
132
133    impl Data for TestEntity {
134        fn id(&self) -> Uuid {
135            self.id
136        }
137        fn tenant_id(&self) -> Uuid {
138            self.tenant_id
139        }
140        fn indexed_fields() -> &'static [&'static str] {
141            &[]
142        }
143        fn field_value(&self, _field: &str) -> Option<crate::core::field::FieldValue> {
144            None
145        }
146    }
147
148    // The traits compile and can be used in generic contexts
149    #[allow(dead_code)]
150    async fn generic_create<T, S>(service: &S, tenant_id: &Uuid, entity: T) -> Result<T>
151    where
152        T: Data,
153        S: DataService<T>,
154    {
155        service.create(tenant_id, entity).await
156    }
157
158    #[test]
159    fn test_traits_compile() {
160        // This test just verifies that the traits are correctly defined
161        // and can be used in generic contexts
162    }
163}