metis_core/application/services/
database.rs1use crate::dal::database::{models::*, repository::DocumentRepository};
2use crate::domain::documents::types::DocumentType;
3use crate::Result;
4
5pub struct DatabaseService {
7 repository: DocumentRepository,
8}
9
10impl DatabaseService {
11 pub fn new(repository: DocumentRepository) -> Self {
12 Self { repository }
13 }
14
15 pub fn create_document(&mut self, document: NewDocument) -> Result<Document> {
17 self.repository.create_document(document)
18 }
19
20 pub fn find_by_filepath(&mut self, filepath: &str) -> Result<Option<Document>> {
22 self.repository.find_by_filepath(filepath)
23 }
24
25 pub fn find_by_id(&mut self, id: &str) -> Result<Option<Document>> {
27 self.repository.find_by_id(id)
28 }
29
30 pub fn update_document(&mut self, filepath: &str, document: &Document) -> Result<Document> {
32 self.repository.update_document(filepath, document)
33 }
34
35 pub fn delete_document(&mut self, filepath: &str) -> Result<bool> {
37 self.repository.delete_document(filepath)
38 }
39
40 pub fn search_documents(&mut self, query: &str) -> Result<Vec<Document>> {
42 self.repository.search_documents(query)
43 }
44
45 pub fn find_by_type(&mut self, doc_type: DocumentType) -> Result<Vec<Document>> {
47 let type_str = doc_type.to_string();
48 self.repository.find_by_type(&type_str)
49 }
50
51 pub fn find_by_tag(&mut self, tag: &str) -> Result<Vec<Document>> {
53 self.repository.find_by_tag(tag)
54 }
55
56 pub fn find_children(&mut self, parent_id: &str) -> Result<Vec<Document>> {
58 self.repository.find_children(parent_id)
59 }
60
61 pub fn find_parent(&mut self, child_id: &str) -> Result<Option<Document>> {
63 self.repository.find_parent(child_id)
64 }
65
66 pub fn create_relationship(
68 &mut self,
69 parent_id: &str,
70 child_id: &str,
71 parent_filepath: &str,
72 child_filepath: &str,
73 ) -> Result<()> {
74 let relationship = DocumentRelationship {
75 parent_id: parent_id.to_string(),
76 child_id: child_id.to_string(),
77 parent_filepath: parent_filepath.to_string(),
78 child_filepath: child_filepath.to_string(),
79 };
80 self.repository.create_relationship(relationship)
81 }
82
83 pub fn document_exists(&mut self, filepath: &str) -> Result<bool> {
85 Ok(self.repository.find_by_filepath(filepath)?.is_some())
86 }
87
88 pub fn count_by_type(&mut self, doc_type: DocumentType) -> Result<usize> {
90 let docs = self.repository.find_by_type(&doc_type.to_string())?;
91 Ok(docs.len())
92 }
93
94 pub fn get_all_id_filepath_pairs(&mut self) -> Result<Vec<(String, String)>> {
96 let mut pairs = Vec::new();
99
100 for doc_type in [
101 DocumentType::Vision,
102 DocumentType::Strategy,
103 DocumentType::Initiative,
104 DocumentType::Task,
105 DocumentType::Adr,
106 ] {
107 let docs = self.repository.find_by_type(&doc_type.to_string())?;
108 for doc in docs {
109 pairs.push((doc.id, doc.filepath));
110 }
111 }
112
113 Ok(pairs)
114 }
115
116 pub fn find_by_strategy_id(&mut self, strategy_id: &str) -> Result<Vec<Document>> {
118 self.repository.find_by_strategy_id(strategy_id)
119 }
120
121 pub fn find_by_initiative_id(&mut self, initiative_id: &str) -> Result<Vec<Document>> {
123 self.repository.find_by_initiative_id(initiative_id)
124 }
125
126 pub fn find_strategy_hierarchy(&mut self, strategy_id: &str) -> Result<Vec<Document>> {
128 self.repository.find_strategy_hierarchy(strategy_id)
129 }
130
131 pub fn find_initiative_hierarchy(&mut self, initiative_id: &str) -> Result<Vec<Document>> {
133 self.repository.find_initiative_hierarchy(initiative_id)
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140 use crate::dal::Database;
141
142 fn setup_service() -> DatabaseService {
143 let db = Database::new(":memory:").expect("Failed to create test database");
144 DatabaseService::new(db.into_repository())
145 }
146
147 fn create_test_document() -> NewDocument {
148 NewDocument {
149 filepath: "/test/doc.md".to_string(),
150 id: "test-doc-1".to_string(),
151 title: "Test Document".to_string(),
152 document_type: "vision".to_string(),
153 created_at: 1609459200.0,
154 updated_at: 1609459200.0,
155 archived: false,
156 exit_criteria_met: false,
157 file_hash: "abc123".to_string(),
158 frontmatter_json: "{}".to_string(),
159 content: Some("Test content".to_string()),
160 phase: "draft".to_string(),
161 strategy_id: None,
162 initiative_id: None,
163 }
164 }
165
166 fn create_test_document_with_lineage(
167 id: &str,
168 doc_type: &str,
169 filepath: &str,
170 strategy_id: Option<String>,
171 initiative_id: Option<String>
172 ) -> NewDocument {
173 NewDocument {
174 filepath: filepath.to_string(),
175 id: id.to_string(),
176 title: format!("Test {}", doc_type),
177 document_type: doc_type.to_string(),
178 created_at: 1609459200.0,
179 updated_at: 1609459200.0,
180 archived: false,
181 exit_criteria_met: false,
182 file_hash: "abc123".to_string(),
183 frontmatter_json: "{}".to_string(),
184 content: Some("Test content".to_string()),
185 phase: "draft".to_string(),
186 strategy_id,
187 initiative_id,
188 }
189 }
190
191 #[test]
192 fn test_database_service_crud() {
193 let mut service = setup_service();
194
195 let new_doc = create_test_document();
197 let created = service.create_document(new_doc).expect("Failed to create");
198 assert_eq!(created.id, "test-doc-1");
199
200 let found = service
202 .find_by_id("test-doc-1")
203 .expect("Failed to find")
204 .expect("Document not found");
205 assert_eq!(found.filepath, "/test/doc.md");
206
207 let mut updated_doc = found.clone();
209 updated_doc.title = "Updated Title".to_string();
210 let updated = service
211 .update_document("/test/doc.md", &updated_doc)
212 .expect("Failed to update");
213 assert_eq!(updated.title, "Updated Title");
214
215 let deleted = service
217 .delete_document("/test/doc.md")
218 .expect("Failed to delete");
219 assert!(deleted);
220
221 assert!(!service
223 .document_exists("/test/doc.md")
224 .expect("Failed to check existence"));
225 }
226
227 #[test]
228 fn test_database_service_relationships() {
229 let mut service = setup_service();
230
231 let parent = NewDocument {
233 id: "parent-1".to_string(),
234 filepath: "/parent.md".to_string(),
235 document_type: "strategy".to_string(),
236 ..create_test_document()
237 };
238
239 let child = NewDocument {
240 id: "child-1".to_string(),
241 filepath: "/child.md".to_string(),
242 document_type: "initiative".to_string(),
243 ..create_test_document()
244 };
245
246 service
247 .create_document(parent)
248 .expect("Failed to create parent");
249 service
250 .create_document(child)
251 .expect("Failed to create child");
252
253 service
255 .create_relationship("parent-1", "child-1", "/parent.md", "/child.md")
256 .expect("Failed to create relationship");
257
258 let children = service
260 .find_children("parent-1")
261 .expect("Failed to find children");
262 assert_eq!(children.len(), 1);
263 assert_eq!(children[0].id, "child-1");
264
265 let parent = service
267 .find_parent("child-1")
268 .expect("Failed to find parent")
269 .expect("Parent not found");
270 assert_eq!(parent.id, "parent-1");
271 }
272
273 #[test]
274 fn test_lineage_queries() {
275 let mut service = setup_service();
276
277 let strategy = create_test_document_with_lineage(
279 "strategy-1",
280 "strategy",
281 "/strategies/strategy-1/strategy.md",
282 None,
283 None
284 );
285 service.create_document(strategy).expect("Failed to create strategy");
286
287 let initiative = create_test_document_with_lineage(
289 "initiative-1",
290 "initiative",
291 "/strategies/strategy-1/initiatives/initiative-1/initiative.md",
292 Some("strategy-1".to_string()),
293 None
294 );
295 service.create_document(initiative).expect("Failed to create initiative");
296
297 let task1 = create_test_document_with_lineage(
299 "task-1",
300 "task",
301 "/strategies/strategy-1/initiatives/initiative-1/tasks/task-1.md",
302 Some("strategy-1".to_string()),
303 Some("initiative-1".to_string())
304 );
305 let task2 = create_test_document_with_lineage(
306 "task-2",
307 "task",
308 "/strategies/strategy-1/initiatives/initiative-1/tasks/task-2.md",
309 Some("strategy-1".to_string()),
310 Some("initiative-1".to_string())
311 );
312 service.create_document(task1).expect("Failed to create task1");
313 service.create_document(task2).expect("Failed to create task2");
314
315 let strategy_docs = service.find_by_strategy_id("strategy-1").expect("Failed to find by strategy");
317 assert_eq!(strategy_docs.len(), 3); let initiative_docs = service.find_by_initiative_id("initiative-1").expect("Failed to find by initiative");
321 assert_eq!(initiative_docs.len(), 2); let strategy_hierarchy = service.find_strategy_hierarchy("strategy-1").expect("Failed to find strategy hierarchy");
325 assert_eq!(strategy_hierarchy.len(), 4); let initiative_hierarchy = service.find_initiative_hierarchy("initiative-1").expect("Failed to find initiative hierarchy");
329 assert_eq!(initiative_hierarchy.len(), 3); let doc_types: Vec<&str> = strategy_hierarchy.iter().map(|d| d.document_type.as_str()).collect();
333 assert!(doc_types.contains(&"strategy"));
334 assert!(doc_types.contains(&"initiative"));
335 assert!(doc_types.iter().filter(|&&t| t == "task").count() == 2);
336 }
337}