1use crate::constraints::ConstraintValidator;
7use crate::Result;
8use axum::extract::{Path, Query};
9use axum::http::StatusCode;
10use axum::response::Json;
11use axum::Extension;
12use serde_json::{json, Value};
13use std::collections::HashMap;
14use std::sync::Arc;
15
16#[derive(Clone)]
21pub struct HandlerContext {
22 pub database: Arc<dyn crate::database::VirtualDatabase + Send + Sync>,
24 pub registry: crate::entities::EntityRegistry,
26 pub session_manager: Option<std::sync::Arc<crate::session::SessionDataManager>>,
28 pub snapshots_dir: Option<std::path::PathBuf>,
30}
31
32fn get_entity_info<'a>(
34 registry: &'a crate::entities::EntityRegistry,
35 entity_name: &str,
36) -> std::result::Result<(&'a crate::entities::Entity, &'a str), (StatusCode, Json<Value>)> {
37 let entity = registry.get(entity_name).ok_or_else(|| {
38 (
39 StatusCode::NOT_FOUND,
40 Json(json!({
41 "error": format!("Entity '{}' not found", entity_name)
42 })),
43 )
44 })?;
45
46 Ok((entity, entity.table_name()))
47}
48
49async fn apply_auto_generation(
51 data: &mut Value,
52 schema: &crate::schema::VbrSchemaDefinition,
53 entity_name: &str,
54 database: &dyn crate::database::VirtualDatabase,
55) -> Result<()> {
56 if let Value::Object(obj) = data {
57 for (field_name, rule) in &schema.auto_generation {
58 if !obj.contains_key(field_name) {
59 let generated_value = match rule {
60 crate::schema::AutoGenerationRule::Uuid => {
61 Value::String(uuid::Uuid::new_v4().to_string())
62 }
63 crate::schema::AutoGenerationRule::Timestamp => {
64 Value::String(chrono::Utc::now().to_rfc3339())
65 }
66 crate::schema::AutoGenerationRule::Date => {
67 Value::String(chrono::Utc::now().date_naive().to_string())
68 }
69 crate::schema::AutoGenerationRule::AutoIncrement => {
70 continue;
72 }
73 crate::schema::AutoGenerationRule::Pattern(pattern) => {
74 let counter = if pattern.contains("increment") {
76 Some(
77 crate::id_generation::get_and_increment_counter(
78 database,
79 entity_name,
80 field_name,
81 )
82 .await?,
83 )
84 } else {
85 None
86 };
87 let id = crate::id_generation::generate_id(
88 rule,
89 entity_name,
90 field_name,
91 counter,
92 )?;
93 Value::String(id)
94 }
95 crate::schema::AutoGenerationRule::Realistic { .. } => {
96 let id =
97 crate::id_generation::generate_id(rule, entity_name, field_name, None)?;
98 Value::String(id)
99 }
100 crate::schema::AutoGenerationRule::Custom(_) => {
101 continue;
103 }
104 };
105 obj.insert(field_name.clone(), generated_value);
106 }
107 }
108 }
109 Ok(())
110}
111
112fn build_where_clause(
114 params: &HashMap<String, String>,
115 entity: &crate::entities::Entity,
116) -> (String, Vec<Value>) {
117 let mut conditions = Vec::new();
118 let mut bind_values = Vec::new();
119
120 for (key, value) in params {
121 if matches!(key.as_str(), "limit" | "offset" | "sort" | "order") {
123 continue;
124 }
125
126 if entity.schema.base.fields.iter().any(|f| f.name == *key) {
128 conditions.push(format!("{} = ?", key));
129 bind_values.push(Value::String(value.clone()));
130 }
131 }
132
133 let where_clause = if conditions.is_empty() {
134 String::new()
135 } else {
136 format!("WHERE {}", conditions.join(" AND "))
137 };
138
139 (where_clause, bind_values)
140}
141
142fn build_order_by(params: &HashMap<String, String>, entity: &crate::entities::Entity) -> String {
144 if let Some(sort_field) = params.get("sort") {
145 if entity.schema.base.fields.iter().any(|f| f.name == *sort_field) {
147 let order = params
148 .get("order")
149 .map(|o| o.to_uppercase())
150 .unwrap_or_else(|| "ASC".to_string());
151 if order == "ASC" || order == "DESC" {
152 return format!("ORDER BY {} {}", sort_field, order);
153 }
154 }
155 }
156 String::new()
157}
158
159fn get_pagination(params: &HashMap<String, String>) -> (Option<usize>, Option<usize>) {
161 let limit = params.get("limit").and_then(|v| v.parse().ok());
162 let offset = params.get("offset").and_then(|v| v.parse().ok());
163 (limit, offset)
164}
165
166pub async fn list_handler(
168 Path(entity_name): Path<String>,
169 Query(params): Query<HashMap<String, String>>,
170 Extension(context): Extension<HandlerContext>,
171) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
172 let (entity, table_name) = get_entity_info(&context.registry, &entity_name)?;
173
174 let (where_clause, bind_values) = build_where_clause(¶ms, entity);
176 let order_by = build_order_by(¶ms, entity);
177 let (limit, offset) = get_pagination(¶ms);
178
179 let mut query = format!("SELECT * FROM {} {}", table_name, where_clause);
181 if !order_by.is_empty() {
182 query.push_str(&format!(" {}", order_by));
183 }
184
185 if let Some(limit_val) = limit {
187 query.push_str(&format!(" LIMIT {}", limit_val));
188 }
189 if let Some(offset_val) = offset {
190 query.push_str(&format!(" OFFSET {}", offset_val));
191 }
192
193 let results = context.database.query(&query, &bind_values).await.map_err(|e| {
195 (
196 StatusCode::INTERNAL_SERVER_ERROR,
197 Json(json!({"error": format!("Database query failed: {}", e)})),
198 )
199 })?;
200
201 let count_query = format!("SELECT COUNT(*) as total FROM {} {}", table_name, where_clause);
203 let count_results = context.database.query(&count_query, &bind_values).await.map_err(|e| {
204 (
205 StatusCode::INTERNAL_SERVER_ERROR,
206 Json(json!({"error": format!("Count query failed: {}", e)})),
207 )
208 })?;
209
210 let total = count_results
211 .first()
212 .and_then(|r| r.get("total"))
213 .and_then(|v| v.as_u64())
214 .unwrap_or(0);
215
216 Ok(Json(json!({
217 "data": results,
218 "total": total,
219 "limit": limit,
220 "offset": offset
221 })))
222}
223
224pub async fn get_handler(
226 Path((entity_name, id)): Path<(String, String)>,
227 Extension(context): Extension<HandlerContext>,
228) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
229 let (_entity, table_name) = get_entity_info(&context.registry, &entity_name)?;
230
231 let primary_key = "id";
233
234 let query = format!("SELECT * FROM {} WHERE {} = ?", table_name, primary_key);
236 let params = vec![Value::String(id.clone())];
237
238 let results = context.database.query(&query, ¶ms).await.map_err(|e| {
240 (
241 StatusCode::INTERNAL_SERVER_ERROR,
242 Json(json!({"error": format!("Database query failed: {}", e)})),
243 )
244 })?;
245
246 if let Some(result) = results.into_iter().next() {
248 Ok(Json(Value::Object(
249 result.into_iter().collect::<serde_json::Map<String, Value>>(),
250 )))
251 } else {
252 Err((
253 StatusCode::NOT_FOUND,
254 Json(json!({
255 "error": format!("{} with id '{}' not found", entity_name, id)
256 })),
257 ))
258 }
259}
260
261pub async fn create_handler(
263 Path(entity_name): Path<String>,
264 Extension(context): Extension<HandlerContext>,
265 Json(mut body): Json<Value>,
266) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
267 let (entity, table_name) = get_entity_info(&context.registry, &entity_name)?;
268
269 apply_auto_generation(&mut body, &entity.schema, &entity_name, context.database.as_ref())
271 .await
272 .map_err(|e| {
273 (
274 StatusCode::INTERNAL_SERVER_ERROR,
275 Json(json!({"error": format!("Auto-generation failed: {}", e)})),
276 )
277 })?;
278
279 let validator = ConstraintValidator;
281 if let Value::Object(obj) = &body {
282 for fk in &entity.schema.foreign_keys {
283 if let Some(fk_value) = obj.get(&fk.field) {
284 validator
285 .validate_foreign_key(
286 context.database.as_ref(),
287 table_name,
288 &fk.field,
289 fk_value,
290 &(fk.target_entity.to_lowercase() + "s"),
291 &fk.target_field,
292 )
293 .await
294 .map_err(|e| {
295 (StatusCode::BAD_REQUEST, Json(json!({"error": e.to_string()})))
296 })?;
297 }
298 }
299 }
300
301 if let Value::Object(obj) = &body {
303 let fields: Vec<String> = obj.keys().cloned().collect();
304 let placeholders: Vec<String> = (0..fields.len()).map(|_| "?".to_string()).collect();
305 let values: Vec<Value> =
306 fields.iter().map(|f| obj.get(f).cloned().unwrap_or(Value::Null)).collect();
307
308 let query = format!(
309 "INSERT INTO {} ({}) VALUES ({})",
310 table_name,
311 fields.join(", "),
312 placeholders.join(", ")
313 );
314
315 let inserted_id = context.database.execute_with_id(&query, &values).await.map_err(|e| {
317 (
318 StatusCode::INTERNAL_SERVER_ERROR,
319 Json(json!({"error": format!("Insert failed: {}", e)})),
320 )
321 })?;
322
323 let primary_key = "id";
325 let select_query = format!("SELECT * FROM {} WHERE {} = ?", table_name, primary_key);
326 let select_results = context
327 .database
328 .query(&select_query, &[Value::String(inserted_id)])
329 .await
330 .map_err(|e| {
331 (
332 StatusCode::INTERNAL_SERVER_ERROR,
333 Json(json!({"error": format!("Failed to fetch created record: {}", e)})),
334 )
335 })?;
336
337 if let Some(result) = select_results.into_iter().next() {
338 Ok(Json(Value::Object(
339 result.into_iter().collect::<serde_json::Map<String, Value>>(),
340 )))
341 } else {
342 Err((
343 StatusCode::INTERNAL_SERVER_ERROR,
344 Json(json!({"error": "Failed to retrieve created record"})),
345 ))
346 }
347 } else {
348 Err((
349 StatusCode::BAD_REQUEST,
350 Json(json!({"error": "Request body must be a JSON object"})),
351 ))
352 }
353}
354
355pub async fn update_handler(
357 Path((entity_name, id)): Path<(String, String)>,
358 Extension(context): Extension<HandlerContext>,
359 Json(body): Json<Value>,
360) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
361 let (entity, table_name) = get_entity_info(&context.registry, &entity_name)?;
362 let primary_key = "id";
363
364 let check_query =
366 format!("SELECT COUNT(*) as count FROM {} WHERE {} = ?", table_name, primary_key);
367 let check_results = context
368 .database
369 .query(&check_query, &[Value::String(id.clone())])
370 .await
371 .map_err(|e| {
372 (
373 StatusCode::INTERNAL_SERVER_ERROR,
374 Json(json!({"error": format!("Database query failed: {}", e)})),
375 )
376 })?;
377
378 let exists = check_results
379 .first()
380 .and_then(|r| r.get("count"))
381 .and_then(|v| v.as_u64())
382 .map(|v| v > 0)
383 .unwrap_or(false);
384
385 if !exists {
386 return Err((
387 StatusCode::NOT_FOUND,
388 Json(json!({
389 "error": format!("{} with id '{}' not found", entity_name, id)
390 })),
391 ));
392 }
393
394 let validator = ConstraintValidator;
396 if let Value::Object(obj) = &body {
397 for fk in &entity.schema.foreign_keys {
398 if let Some(fk_value) = obj.get(&fk.field) {
399 validator
400 .validate_foreign_key(
401 context.database.as_ref(),
402 table_name,
403 &fk.field,
404 fk_value,
405 &(fk.target_entity.to_lowercase() + "s"),
406 &fk.target_field,
407 )
408 .await
409 .map_err(|e| {
410 (StatusCode::BAD_REQUEST, Json(json!({"error": e.to_string()})))
411 })?;
412 }
413 }
414 }
415
416 if let Value::Object(obj) = &body {
418 let mut set_clauses = Vec::new();
419 let mut values = Vec::new();
420
421 for (field, value) in obj.iter() {
422 if field != primary_key {
423 set_clauses.push(format!("{} = ?", field));
425 values.push(value.clone());
426 }
427 }
428
429 if set_clauses.is_empty() {
430 return Err((StatusCode::BAD_REQUEST, Json(json!({"error": "No fields to update"}))));
431 }
432
433 values.push(Value::String(id.clone()));
434
435 let query = format!(
436 "UPDATE {} SET {} WHERE {} = ?",
437 table_name,
438 set_clauses.join(", "),
439 primary_key
440 );
441
442 context.database.execute(&query, &values).await.map_err(|e| {
444 (
445 StatusCode::INTERNAL_SERVER_ERROR,
446 Json(json!({"error": format!("Update failed: {}", e)})),
447 )
448 })?;
449
450 let select_query = format!("SELECT * FROM {} WHERE {} = ?", table_name, primary_key);
452 let select_results =
453 context.database.query(&select_query, &[Value::String(id)]).await.map_err(|e| {
454 (
455 StatusCode::INTERNAL_SERVER_ERROR,
456 Json(json!({"error": format!("Failed to fetch updated record: {}", e)})),
457 )
458 })?;
459
460 if let Some(result) = select_results.into_iter().next() {
461 Ok(Json(Value::Object(
462 result.into_iter().collect::<serde_json::Map<String, Value>>(),
463 )))
464 } else {
465 Err((
466 StatusCode::INTERNAL_SERVER_ERROR,
467 Json(json!({"error": "Failed to retrieve updated record"})),
468 ))
469 }
470 } else {
471 Err((
472 StatusCode::BAD_REQUEST,
473 Json(json!({"error": "Request body must be a JSON object"})),
474 ))
475 }
476}
477
478pub async fn patch_handler(
480 Path((entity_name, id)): Path<(String, String)>,
481 Extension(context): Extension<HandlerContext>,
482 Json(body): Json<Value>,
483) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
484 update_handler(Path((entity_name.clone(), id.clone())), Extension(context), Json(body)).await
488}
489
490pub async fn delete_handler(
492 Path((entity_name, id)): Path<(String, String)>,
493 Extension(context): Extension<HandlerContext>,
494) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
495 let (_entity, table_name) = get_entity_info(&context.registry, &entity_name)?;
496 let primary_key = "id";
497
498 let check_query =
500 format!("SELECT COUNT(*) as count FROM {} WHERE {} = ?", table_name, primary_key);
501 let check_results = context
502 .database
503 .query(&check_query, &[Value::String(id.clone())])
504 .await
505 .map_err(|e| {
506 (
507 StatusCode::INTERNAL_SERVER_ERROR,
508 Json(json!({"error": format!("Database query failed: {}", e)})),
509 )
510 })?;
511
512 let exists = check_results
513 .first()
514 .and_then(|r| r.get("count"))
515 .and_then(|v| v.as_u64())
516 .map(|v| v > 0)
517 .unwrap_or(false);
518
519 if !exists {
520 return Err((
521 StatusCode::NOT_FOUND,
522 Json(json!({
523 "error": format!("{} with id '{}' not found", entity_name, id)
524 })),
525 ));
526 }
527
528 let query = format!("DELETE FROM {} WHERE {} = ?", table_name, primary_key);
530 let params = vec![Value::String(id.clone())];
531
532 let rows_affected = context.database.execute(&query, ¶ms).await.map_err(|e| {
534 (
535 StatusCode::INTERNAL_SERVER_ERROR,
536 Json(json!({"error": format!("Delete failed: {}", e)})),
537 )
538 })?;
539
540 if rows_affected > 0 {
541 Ok(Json(json!({
542 "message": format!("{} with id '{}' deleted successfully", entity_name, id),
543 "id": id
544 })))
545 } else {
546 Err((
547 StatusCode::NOT_FOUND,
548 Json(json!({
549 "error": format!("{} with id '{}' not found", entity_name, id)
550 })),
551 ))
552 }
553}
554
555pub async fn get_relationship_handler(
563 Path((entity_name, id, relationship_name)): Path<(String, String, String)>,
564 Query(params): Query<HashMap<String, String>>,
565 Extension(context): Extension<HandlerContext>,
566) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
567 let (entity, table_name) = get_entity_info(&context.registry, &entity_name)?;
568 let primary_key = "id";
569
570 let check_query =
572 format!("SELECT COUNT(*) as count FROM {} WHERE {} = ?", table_name, primary_key);
573 let check_results = context
574 .database
575 .query(&check_query, &[Value::String(id.clone())])
576 .await
577 .map_err(|e| {
578 (
579 StatusCode::INTERNAL_SERVER_ERROR,
580 Json(json!({"error": format!("Database query failed: {}", e)})),
581 )
582 })?;
583
584 let exists = check_results
585 .first()
586 .and_then(|r| r.get("count"))
587 .and_then(|v| v.as_u64())
588 .map(|v| v > 0)
589 .unwrap_or(false);
590
591 if !exists {
592 return Err((
593 StatusCode::NOT_FOUND,
594 Json(json!({
595 "error": format!("{} with id '{}' not found", entity_name, id)
596 })),
597 ));
598 }
599
600 if let Some(target_entity) = context.registry.get(&relationship_name) {
605 if let Some(fk) = target_entity
607 .schema
608 .foreign_keys
609 .iter()
610 .find(|fk| fk.target_entity == entity_name)
611 {
612 let target_table = target_entity.table_name();
615
616 let (where_clause, mut bind_values) = build_where_clause(¶ms, target_entity);
618 let order_by = build_order_by(¶ms, target_entity);
619 let (limit, offset) = get_pagination(¶ms);
620
621 let fk_condition = if where_clause.is_empty() {
623 format!("WHERE {} = ?", fk.field)
624 } else {
625 format!("{} AND {} = ?", where_clause, fk.field)
626 };
627 bind_values.push(Value::String(id.clone()));
628
629 let mut query = format!("SELECT * FROM {} {}", target_table, fk_condition);
630 if !order_by.is_empty() {
631 query.push_str(&format!(" {}", order_by));
632 }
633 if let Some(limit_val) = limit {
634 query.push_str(&format!(" LIMIT {}", limit_val));
635 }
636 if let Some(offset_val) = offset {
637 query.push_str(&format!(" OFFSET {}", offset_val));
638 }
639
640 let results = context.database.query(&query, &bind_values).await.map_err(|e| {
641 (
642 StatusCode::INTERNAL_SERVER_ERROR,
643 Json(json!({"error": format!("Database query failed: {}", e)})),
644 )
645 })?;
646
647 let count_query =
649 format!("SELECT COUNT(*) as total FROM {} {}", target_table, fk_condition);
650 let count_results =
651 context.database.query(&count_query, &bind_values).await.map_err(|e| {
652 (
653 StatusCode::INTERNAL_SERVER_ERROR,
654 Json(json!({"error": format!("Count query failed: {}", e)})),
655 )
656 })?;
657
658 let total = count_results
659 .first()
660 .and_then(|r| r.get("total"))
661 .and_then(|v| v.as_u64())
662 .unwrap_or(0);
663
664 return Ok(Json(json!({
665 "data": results,
666 "total": total,
667 "relationship": relationship_name,
668 "parent_entity": entity_name,
669 "parent_id": id
670 })));
671 }
672 }
673
674 if let Some(fk) = entity.schema.foreign_keys.iter().find(|fk| {
680 fk.target_entity.to_lowercase() == relationship_name.to_lowercase()
682 || fk.field == relationship_name
683 || fk.field == format!("{}_id", relationship_name)
684 }) {
685 let current_query = format!("SELECT * FROM {} WHERE {} = ?", table_name, primary_key);
688 let current_results = context
689 .database
690 .query(¤t_query, &[Value::String(id.clone())])
691 .await
692 .map_err(|e| {
693 (
694 StatusCode::INTERNAL_SERVER_ERROR,
695 Json(json!({"error": format!("Database query failed: {}", e)})),
696 )
697 })?;
698
699 if let Some(current_record) = current_results.into_iter().next() {
700 let fk_value = current_record.get(&fk.field).ok_or_else(|| {
702 (
703 StatusCode::BAD_REQUEST,
704 Json(json!({
705 "error": format!("Foreign key field '{}' not found in record", fk.field)
706 })),
707 )
708 })?;
709
710 let target_entity = context.registry.get(&fk.target_entity).ok_or_else(|| {
712 (
713 StatusCode::NOT_FOUND,
714 Json(json!({
715 "error": format!("Target entity '{}' not found", fk.target_entity)
716 })),
717 )
718 })?;
719
720 let target_table = target_entity.table_name();
721 let target_primary_key = "id";
722
723 let target_query =
725 format!("SELECT * FROM {} WHERE {} = ?", target_table, target_primary_key);
726 let target_results =
727 context.database.query(&target_query, &[fk_value.clone()]).await.map_err(|e| {
728 (
729 StatusCode::INTERNAL_SERVER_ERROR,
730 Json(json!({"error": format!("Database query failed: {}", e)})),
731 )
732 })?;
733
734 if let Some(target_record) = target_results.into_iter().next() {
735 return Ok(Json(Value::Object(
736 target_record.into_iter().collect::<serde_json::Map<String, Value>>(),
737 )));
738 } else {
739 return Err((
740 StatusCode::NOT_FOUND,
741 Json(json!({
742 "error": format!("Related {} not found", relationship_name)
743 })),
744 ));
745 }
746 } else {
747 return Err((
748 StatusCode::NOT_FOUND,
749 Json(json!({
750 "error": format!("{} with id '{}' not found", entity_name, id)
751 })),
752 ));
753 }
754 }
755
756 for m2m in &entity.schema.many_to_many {
760 let is_entity_a = m2m.entity_a.to_lowercase() == relationship_name.to_lowercase();
762 let is_entity_b = m2m.entity_b.to_lowercase() == relationship_name.to_lowercase();
763
764 if is_entity_a || is_entity_b {
765 let target_entity_name = if is_entity_a {
767 &m2m.entity_a
768 } else {
769 &m2m.entity_b
770 };
771
772 let target_entity = context.registry.get(target_entity_name).ok_or_else(|| {
773 (
774 StatusCode::NOT_FOUND,
775 Json(json!({
776 "error": format!("Target entity '{}' not found", target_entity_name)
777 })),
778 )
779 })?;
780
781 let junction_table = m2m.junction_table.as_ref().ok_or_else(|| {
782 (
783 StatusCode::INTERNAL_SERVER_ERROR,
784 Json(json!({
785 "error": "Junction table name not specified for many-to-many relationship"
786 })),
787 )
788 })?;
789
790 let fk_field = if entity_name.to_lowercase() == m2m.entity_a.to_lowercase() {
792 &m2m.entity_a_field
793 } else {
794 &m2m.entity_b_field
795 };
796
797 let target_fk_field = if is_entity_a {
798 &m2m.entity_b_field
799 } else {
800 &m2m.entity_a_field
801 };
802
803 let target_table = target_entity.table_name();
804
805 let (where_clause, mut bind_values) = build_where_clause(¶ms, target_entity);
807 let order_by = build_order_by(¶ms, target_entity);
808 let (limit, offset) = get_pagination(¶ms);
809
810 let mut query = format!(
812 "SELECT t.* FROM {} t INNER JOIN {} j ON t.id = j.{} WHERE j.{} = ?",
813 target_table, junction_table, target_fk_field, fk_field
814 );
815
816 bind_values.push(Value::String(id.clone()));
817
818 if !where_clause.is_empty() {
819 query.push_str(&format!(" AND {}", where_clause));
820 }
821
822 if !order_by.is_empty() {
823 query.push_str(&format!(" {}", order_by));
824 }
825
826 if let Some(limit_val) = limit {
827 query.push_str(&format!(" LIMIT {}", limit_val));
828 }
829
830 if let Some(offset_val) = offset {
831 query.push_str(&format!(" OFFSET {}", offset_val));
832 }
833
834 let results = context.database.query(&query, &bind_values).await.map_err(|e| {
835 (
836 StatusCode::INTERNAL_SERVER_ERROR,
837 Json(json!({"error": format!("Database query failed: {}", e)})),
838 )
839 })?;
840
841 let count_query = format!(
843 "SELECT COUNT(*) as total FROM {} t INNER JOIN {} j ON t.id = j.{} WHERE j.{} = ?",
844 target_table, junction_table, target_fk_field, fk_field
845 );
846 let count_results =
847 context.database.query(&count_query, &bind_values).await.map_err(|e| {
848 (
849 StatusCode::INTERNAL_SERVER_ERROR,
850 Json(json!({"error": format!("Count query failed: {}", e)})),
851 )
852 })?;
853
854 let total = count_results
855 .first()
856 .and_then(|r| r.get("total"))
857 .and_then(|v| v.as_u64())
858 .unwrap_or(0);
859
860 return Ok(Json(json!({
861 "data": results,
862 "total": total,
863 "relationship": relationship_name,
864 "parent_entity": entity_name,
865 "parent_id": id,
866 "relationship_type": "many_to_many"
867 })));
868 }
869 }
870
871 Err((
873 StatusCode::NOT_FOUND,
874 Json(json!({
875 "error": format!("Relationship '{}' not found for entity '{}'", relationship_name, entity_name)
876 })),
877 ))
878}
879
880#[derive(serde::Deserialize)]
882pub struct CreateSnapshotRequest {
883 pub name: String,
884 pub description: Option<String>,
885}
886
887pub async fn create_snapshot_handler(
888 Extension(context): Extension<HandlerContext>,
889 Json(body): Json<CreateSnapshotRequest>,
890) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
891 let snapshots_dir = context.snapshots_dir.as_ref().ok_or_else(|| {
892 (
893 StatusCode::BAD_REQUEST,
894 Json(json!({"error": "Snapshots directory not configured"})),
895 )
896 })?;
897
898 let manager = crate::snapshots::SnapshotManager::new(snapshots_dir);
899 let metadata = manager
900 .create_snapshot(&body.name, body.description, context.database.as_ref(), &context.registry)
901 .await
902 .map_err(|e| {
903 (
904 StatusCode::INTERNAL_SERVER_ERROR,
905 Json(json!({"error": format!("Failed to create snapshot: {}", e)})),
906 )
907 })?;
908
909 Ok(Json(serde_json::to_value(&metadata).unwrap()))
910}
911
912pub async fn list_snapshots_handler(
914 Extension(context): Extension<HandlerContext>,
915) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
916 let snapshots_dir = context.snapshots_dir.as_ref().ok_or_else(|| {
917 (
918 StatusCode::BAD_REQUEST,
919 Json(json!({"error": "Snapshots directory not configured"})),
920 )
921 })?;
922
923 let manager = crate::snapshots::SnapshotManager::new(snapshots_dir);
924 let snapshots = manager.list_snapshots().await.map_err(|e| {
925 (
926 StatusCode::INTERNAL_SERVER_ERROR,
927 Json(json!({"error": format!("Failed to list snapshots: {}", e)})),
928 )
929 })?;
930
931 Ok(Json(serde_json::to_value(&snapshots).unwrap()))
932}
933
934pub async fn restore_snapshot_handler(
936 Path(name): Path<String>,
937 Extension(context): Extension<HandlerContext>,
938) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
939 let snapshots_dir = context.snapshots_dir.as_ref().ok_or_else(|| {
940 (
941 StatusCode::BAD_REQUEST,
942 Json(json!({"error": "Snapshots directory not configured"})),
943 )
944 })?;
945
946 let manager = crate::snapshots::SnapshotManager::new(snapshots_dir);
947 manager
948 .restore_snapshot(&name, context.database.as_ref(), &context.registry)
949 .await
950 .map_err(|e| {
951 (
952 StatusCode::INTERNAL_SERVER_ERROR,
953 Json(json!({"error": format!("Failed to restore snapshot: {}", e)})),
954 )
955 })?;
956
957 Ok(Json(json!({"message": format!("Snapshot '{}' restored successfully", name)})))
958}
959
960pub async fn delete_snapshot_handler(
962 Path(name): Path<String>,
963 Extension(context): Extension<HandlerContext>,
964) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
965 let snapshots_dir = context.snapshots_dir.as_ref().ok_or_else(|| {
966 (
967 StatusCode::BAD_REQUEST,
968 Json(json!({"error": "Snapshots directory not configured"})),
969 )
970 })?;
971
972 let manager = crate::snapshots::SnapshotManager::new(snapshots_dir);
973 manager.delete_snapshot(&name).await.map_err(|e| {
974 (
975 StatusCode::INTERNAL_SERVER_ERROR,
976 Json(json!({"error": format!("Failed to delete snapshot: {}", e)})),
977 )
978 })?;
979
980 Ok(Json(json!({"message": format!("Snapshot '{}' deleted successfully", name)})))
981}
982
983pub async fn reset_handler(
985 Extension(context): Extension<HandlerContext>,
986) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
987 crate::snapshots::reset_database(context.database.as_ref(), &context.registry)
988 .await
989 .map_err(|e| {
990 (
991 StatusCode::INTERNAL_SERVER_ERROR,
992 Json(json!({"error": format!("Failed to reset database: {}", e)})),
993 )
994 })?;
995
996 Ok(Json(json!({"message": "Database reset successfully"})))
997}
998
999#[cfg(test)]
1000mod tests {
1001 use super::*;
1002 use crate::database::{InMemoryDatabase, VirtualDatabase};
1003 use crate::entities::{Entity, EntityRegistry};
1004 use crate::migration::MigrationManager;
1005 use crate::schema::VbrSchemaDefinition;
1006 use mockforge_data::{FieldDefinition, SchemaDefinition};
1007 use std::sync::Arc;
1008
1009 async fn setup_test_database() -> (Arc<dyn VirtualDatabase + Send + Sync>, EntityRegistry) {
1010 let mut db = InMemoryDatabase::new().await.unwrap();
1011 db.initialize().await.unwrap();
1012 let registry = EntityRegistry::new();
1013 (Arc::new(db), registry)
1014 }
1015
1016 async fn create_test_entity(
1017 database: &dyn VirtualDatabase,
1018 registry: &mut EntityRegistry,
1019 entity_name: &str,
1020 ) {
1021 let base_schema = SchemaDefinition::new(entity_name.to_string())
1022 .with_field(FieldDefinition::new("id".to_string(), "string".to_string()))
1023 .with_field(FieldDefinition::new("name".to_string(), "string".to_string()));
1024
1025 let vbr_schema = VbrSchemaDefinition::new(base_schema);
1026 let entity = Entity::new(entity_name.to_string(), vbr_schema);
1027
1028 let manager = MigrationManager::new();
1029 let create_sql = manager.generate_create_table(&entity).unwrap();
1030 database.create_table(&create_sql).await.unwrap();
1031
1032 registry.register(entity).unwrap();
1033 }
1034
1035 fn create_test_context(
1036 database: Arc<dyn VirtualDatabase + Send + Sync>,
1037 registry: EntityRegistry,
1038 ) -> HandlerContext {
1039 HandlerContext {
1040 database,
1041 registry,
1042 session_manager: None,
1043 snapshots_dir: None,
1044 }
1045 }
1046
1047 #[test]
1049 fn test_build_where_clause_empty() {
1050 let params = HashMap::new();
1051 let base_schema = SchemaDefinition::new("Test".to_string());
1052 let vbr_schema = VbrSchemaDefinition::new(base_schema);
1053 let entity = Entity::new("Test".to_string(), vbr_schema);
1054
1055 let (where_clause, bind_values) = build_where_clause(¶ms, &entity);
1056 assert_eq!(where_clause, "");
1057 assert_eq!(bind_values.len(), 0);
1058 }
1059
1060 #[test]
1061 fn test_build_where_clause_with_params() {
1062 let mut params = HashMap::new();
1063 params.insert("name".to_string(), "John".to_string());
1064
1065 let base_schema = SchemaDefinition::new("User".to_string())
1066 .with_field(FieldDefinition::new("name".to_string(), "string".to_string()));
1067 let vbr_schema = VbrSchemaDefinition::new(base_schema);
1068 let entity = Entity::new("User".to_string(), vbr_schema);
1069
1070 let (where_clause, bind_values) = build_where_clause(¶ms, &entity);
1071 assert!(where_clause.contains("WHERE"));
1072 assert!(where_clause.contains("name = ?"));
1073 assert_eq!(bind_values.len(), 1);
1074 }
1075
1076 #[test]
1077 fn test_build_where_clause_ignores_pagination() {
1078 let mut params = HashMap::new();
1079 params.insert("limit".to_string(), "10".to_string());
1080 params.insert("offset".to_string(), "5".to_string());
1081 params.insert("sort".to_string(), "name".to_string());
1082
1083 let base_schema = SchemaDefinition::new("Test".to_string());
1084 let vbr_schema = VbrSchemaDefinition::new(base_schema);
1085 let entity = Entity::new("Test".to_string(), vbr_schema);
1086
1087 let (where_clause, bind_values) = build_where_clause(¶ms, &entity);
1088 assert_eq!(where_clause, "");
1089 assert_eq!(bind_values.len(), 0);
1090 }
1091
1092 #[test]
1093 fn test_build_order_by_no_sort() {
1094 let params = HashMap::new();
1095 let base_schema = SchemaDefinition::new("Test".to_string());
1096 let vbr_schema = VbrSchemaDefinition::new(base_schema);
1097 let entity = Entity::new("Test".to_string(), vbr_schema);
1098
1099 let order_by = build_order_by(¶ms, &entity);
1100 assert_eq!(order_by, "");
1101 }
1102
1103 #[test]
1104 fn test_build_order_by_with_sort() {
1105 let mut params = HashMap::new();
1106 params.insert("sort".to_string(), "name".to_string());
1107
1108 let base_schema = SchemaDefinition::new("User".to_string())
1109 .with_field(FieldDefinition::new("name".to_string(), "string".to_string()));
1110 let vbr_schema = VbrSchemaDefinition::new(base_schema);
1111 let entity = Entity::new("User".to_string(), vbr_schema);
1112
1113 let order_by = build_order_by(¶ms, &entity);
1114 assert_eq!(order_by, "ORDER BY name ASC");
1115 }
1116
1117 #[test]
1118 fn test_build_order_by_with_sort_desc() {
1119 let mut params = HashMap::new();
1120 params.insert("sort".to_string(), "name".to_string());
1121 params.insert("order".to_string(), "DESC".to_string());
1122
1123 let base_schema = SchemaDefinition::new("User".to_string())
1124 .with_field(FieldDefinition::new("name".to_string(), "string".to_string()));
1125 let vbr_schema = VbrSchemaDefinition::new(base_schema);
1126 let entity = Entity::new("User".to_string(), vbr_schema);
1127
1128 let order_by = build_order_by(¶ms, &entity);
1129 assert_eq!(order_by, "ORDER BY name DESC");
1130 }
1131
1132 #[test]
1133 fn test_build_order_by_invalid_field() {
1134 let mut params = HashMap::new();
1135 params.insert("sort".to_string(), "invalid_field".to_string());
1136
1137 let base_schema = SchemaDefinition::new("User".to_string())
1138 .with_field(FieldDefinition::new("name".to_string(), "string".to_string()));
1139 let vbr_schema = VbrSchemaDefinition::new(base_schema);
1140 let entity = Entity::new("User".to_string(), vbr_schema);
1141
1142 let order_by = build_order_by(¶ms, &entity);
1143 assert_eq!(order_by, "");
1144 }
1145
1146 #[test]
1147 fn test_build_order_by_invalid_order() {
1148 let mut params = HashMap::new();
1149 params.insert("sort".to_string(), "name".to_string());
1150 params.insert("order".to_string(), "INVALID".to_string());
1151
1152 let base_schema = SchemaDefinition::new("User".to_string())
1153 .with_field(FieldDefinition::new("name".to_string(), "string".to_string()));
1154 let vbr_schema = VbrSchemaDefinition::new(base_schema);
1155 let entity = Entity::new("User".to_string(), vbr_schema);
1156
1157 let order_by = build_order_by(¶ms, &entity);
1158 assert_eq!(order_by, "");
1159 }
1160
1161 #[test]
1162 fn test_get_pagination_no_params() {
1163 let params = HashMap::new();
1164 let (limit, offset) = get_pagination(¶ms);
1165 assert_eq!(limit, None);
1166 assert_eq!(offset, None);
1167 }
1168
1169 #[test]
1170 fn test_get_pagination_with_limit() {
1171 let mut params = HashMap::new();
1172 params.insert("limit".to_string(), "10".to_string());
1173
1174 let (limit, offset) = get_pagination(¶ms);
1175 assert_eq!(limit, Some(10));
1176 assert_eq!(offset, None);
1177 }
1178
1179 #[test]
1180 fn test_get_pagination_with_limit_and_offset() {
1181 let mut params = HashMap::new();
1182 params.insert("limit".to_string(), "20".to_string());
1183 params.insert("offset".to_string(), "5".to_string());
1184
1185 let (limit, offset) = get_pagination(¶ms);
1186 assert_eq!(limit, Some(20));
1187 assert_eq!(offset, Some(5));
1188 }
1189
1190 #[test]
1191 fn test_get_pagination_invalid_values() {
1192 let mut params = HashMap::new();
1193 params.insert("limit".to_string(), "abc".to_string());
1194 params.insert("offset".to_string(), "xyz".to_string());
1195
1196 let (limit, offset) = get_pagination(¶ms);
1197 assert_eq!(limit, None);
1198 assert_eq!(offset, None);
1199 }
1200
1201 #[test]
1203 fn test_handler_context_clone() {
1204 let (database, registry) = tokio::runtime::Runtime::new()
1205 .unwrap()
1206 .block_on(async { setup_test_database().await });
1207
1208 let context = create_test_context(database, registry);
1209 let cloned = context.clone();
1210
1211 assert!(Arc::ptr_eq(&context.database, &cloned.database));
1212 }
1213
1214 #[tokio::test]
1216 async fn test_get_entity_info_success() {
1217 let (database, mut registry) = setup_test_database().await;
1218 create_test_entity(database.as_ref(), &mut registry, "User").await;
1219
1220 let result = get_entity_info(®istry, "User");
1221 assert!(result.is_ok());
1222 let (entity, table_name) = result.unwrap();
1223 assert_eq!(entity.name(), "User");
1224 assert_eq!(table_name, "users");
1225 }
1226
1227 #[tokio::test]
1228 async fn test_get_entity_info_not_found() {
1229 let (_database, registry) = setup_test_database().await;
1230
1231 let result = get_entity_info(®istry, "NonExistent");
1232 assert!(result.is_err());
1233 let (status, _) = result.unwrap_err();
1234 assert_eq!(status, StatusCode::NOT_FOUND);
1235 }
1236
1237 #[tokio::test]
1239 async fn test_apply_auto_generation_uuid() {
1240 let (database, _registry) = setup_test_database().await;
1241 let mut data = json!({});
1242 let base_schema = SchemaDefinition::new("Test".to_string());
1243 let mut vbr_schema = VbrSchemaDefinition::new(base_schema);
1244 vbr_schema
1245 .auto_generation
1246 .insert("id".to_string(), crate::schema::AutoGenerationRule::Uuid);
1247
1248 apply_auto_generation(&mut data, &vbr_schema, "Test", database.as_ref())
1249 .await
1250 .unwrap();
1251
1252 assert!(data.as_object().unwrap().contains_key("id"));
1253 assert!(data["id"].is_string());
1254 }
1255
1256 #[tokio::test]
1257 async fn test_apply_auto_generation_timestamp() {
1258 let (database, _registry) = setup_test_database().await;
1259 let mut data = json!({});
1260 let base_schema = SchemaDefinition::new("Test".to_string());
1261 let mut vbr_schema = VbrSchemaDefinition::new(base_schema);
1262 vbr_schema
1263 .auto_generation
1264 .insert("created_at".to_string(), crate::schema::AutoGenerationRule::Timestamp);
1265
1266 apply_auto_generation(&mut data, &vbr_schema, "Test", database.as_ref())
1267 .await
1268 .unwrap();
1269
1270 assert!(data.as_object().unwrap().contains_key("created_at"));
1271 assert!(data["created_at"].is_string());
1272 }
1273
1274 #[tokio::test]
1275 async fn test_apply_auto_generation_date() {
1276 let (database, _registry) = setup_test_database().await;
1277 let mut data = json!({});
1278 let base_schema = SchemaDefinition::new("Test".to_string());
1279 let mut vbr_schema = VbrSchemaDefinition::new(base_schema);
1280 vbr_schema
1281 .auto_generation
1282 .insert("date_field".to_string(), crate::schema::AutoGenerationRule::Date);
1283
1284 apply_auto_generation(&mut data, &vbr_schema, "Test", database.as_ref())
1285 .await
1286 .unwrap();
1287
1288 assert!(data.as_object().unwrap().contains_key("date_field"));
1289 assert!(data["date_field"].is_string());
1290 }
1291
1292 #[tokio::test]
1293 async fn test_apply_auto_generation_skips_existing() {
1294 let (database, _registry) = setup_test_database().await;
1295 let mut data = json!({"id": "existing-id"});
1296 let base_schema = SchemaDefinition::new("Test".to_string());
1297 let mut vbr_schema = VbrSchemaDefinition::new(base_schema);
1298 vbr_schema
1299 .auto_generation
1300 .insert("id".to_string(), crate::schema::AutoGenerationRule::Uuid);
1301
1302 apply_auto_generation(&mut data, &vbr_schema, "Test", database.as_ref())
1303 .await
1304 .unwrap();
1305
1306 assert_eq!(data["id"], "existing-id");
1307 }
1308
1309 #[test]
1311 fn test_create_snapshot_request_deserialize() {
1312 let json = r#"{"name": "test-snapshot", "description": "Test description"}"#;
1313 let request: CreateSnapshotRequest = serde_json::from_str(json).unwrap();
1314 assert_eq!(request.name, "test-snapshot");
1315 assert_eq!(request.description, Some("Test description".to_string()));
1316 }
1317
1318 #[test]
1319 fn test_create_snapshot_request_deserialize_no_description() {
1320 let json = r#"{"name": "test-snapshot"}"#;
1321 let request: CreateSnapshotRequest = serde_json::from_str(json).unwrap();
1322 assert_eq!(request.name, "test-snapshot");
1323 assert_eq!(request.description, None);
1324 }
1325
1326 #[tokio::test]
1328 async fn test_list_handler_empty() {
1329 let (database, mut registry) = setup_test_database().await;
1330 create_test_entity(database.as_ref(), &mut registry, "User").await;
1331
1332 let context = create_test_context(database, registry);
1333 let params = HashMap::new();
1334
1335 let result =
1336 list_handler(Path("User".to_string()), Query(params), Extension(context)).await;
1337
1338 assert!(result.is_ok());
1339 let json_value = result.unwrap().0;
1340 assert!(json_value["data"].is_array());
1341 assert_eq!(json_value["total"], 0);
1342 }
1343
1344 #[tokio::test]
1345 async fn test_list_handler_entity_not_found() {
1346 let (database, registry) = setup_test_database().await;
1347 let context = create_test_context(database, registry);
1348 let params = HashMap::new();
1349
1350 let result =
1351 list_handler(Path("NonExistent".to_string()), Query(params), Extension(context)).await;
1352
1353 assert!(result.is_err());
1354 let (status, _) = result.unwrap_err();
1355 assert_eq!(status, StatusCode::NOT_FOUND);
1356 }
1357
1358 #[tokio::test]
1359 async fn test_get_handler_not_found() {
1360 let (database, mut registry) = setup_test_database().await;
1361 create_test_entity(database.as_ref(), &mut registry, "User").await;
1362
1363 let context = create_test_context(database, registry);
1364
1365 let result = get_handler(
1366 Path(("User".to_string(), "nonexistent-id".to_string())),
1367 Extension(context),
1368 )
1369 .await;
1370
1371 assert!(result.is_err());
1372 let (status, _) = result.unwrap_err();
1373 assert_eq!(status, StatusCode::NOT_FOUND);
1374 }
1375
1376 #[tokio::test]
1377 async fn test_create_handler_invalid_body() {
1378 let (database, mut registry) = setup_test_database().await;
1379 create_test_entity(database.as_ref(), &mut registry, "User").await;
1380
1381 let context = create_test_context(database, registry);
1382 let body = json!("not an object");
1383
1384 let result = create_handler(Path("User".to_string()), Extension(context), Json(body)).await;
1385
1386 assert!(result.is_err());
1387 let (status, _) = result.unwrap_err();
1388 assert_eq!(status, StatusCode::BAD_REQUEST);
1389 }
1390
1391 #[tokio::test]
1392 async fn test_update_handler_not_found() {
1393 let (database, mut registry) = setup_test_database().await;
1394 create_test_entity(database.as_ref(), &mut registry, "User").await;
1395
1396 let context = create_test_context(database, registry);
1397 let body = json!({"name": "Updated"});
1398
1399 let result = update_handler(
1400 Path(("User".to_string(), "nonexistent-id".to_string())),
1401 Extension(context),
1402 Json(body),
1403 )
1404 .await;
1405
1406 assert!(result.is_err());
1407 let (status, _) = result.unwrap_err();
1408 assert_eq!(status, StatusCode::NOT_FOUND);
1409 }
1410
1411 #[tokio::test]
1412 async fn test_update_handler_invalid_body() {
1413 let (database, mut registry) = setup_test_database().await;
1414 create_test_entity(database.as_ref(), &mut registry, "User").await;
1415
1416 let context = create_test_context(database, registry);
1417 let body = json!("not an object");
1418
1419 let result = update_handler(
1420 Path(("User".to_string(), "some-id".to_string())),
1421 Extension(context),
1422 Json(body),
1423 )
1424 .await;
1425
1426 assert!(result.is_err());
1427 let (status, _) = result.unwrap_err();
1428 assert_eq!(status, StatusCode::BAD_REQUEST);
1429 }
1430
1431 #[tokio::test]
1432 async fn test_delete_handler_not_found() {
1433 let (database, mut registry) = setup_test_database().await;
1434 create_test_entity(database.as_ref(), &mut registry, "User").await;
1435
1436 let context = create_test_context(database, registry);
1437
1438 let result = delete_handler(
1439 Path(("User".to_string(), "nonexistent-id".to_string())),
1440 Extension(context),
1441 )
1442 .await;
1443
1444 assert!(result.is_err());
1445 let (status, _) = result.unwrap_err();
1446 assert_eq!(status, StatusCode::NOT_FOUND);
1447 }
1448
1449 #[tokio::test]
1450 async fn test_get_relationship_handler_entity_not_found() {
1451 let (database, registry) = setup_test_database().await;
1452 let context = create_test_context(database, registry);
1453 let params = HashMap::new();
1454
1455 let result = get_relationship_handler(
1456 Path(("User".to_string(), "123".to_string(), "orders".to_string())),
1457 Query(params),
1458 Extension(context),
1459 )
1460 .await;
1461
1462 assert!(result.is_err());
1463 let (status, _) = result.unwrap_err();
1464 assert_eq!(status, StatusCode::NOT_FOUND);
1465 }
1466
1467 #[tokio::test]
1468 async fn test_snapshot_handlers_no_directory() {
1469 let (database, registry) = setup_test_database().await;
1470 let context = create_test_context(database, registry);
1471
1472 let body = CreateSnapshotRequest {
1474 name: "test".to_string(),
1475 description: None,
1476 };
1477 let result = create_snapshot_handler(Extension(context.clone()), Json(body)).await;
1478 assert!(result.is_err());
1479
1480 let result = list_snapshots_handler(Extension(context.clone())).await;
1482 assert!(result.is_err());
1483
1484 let result =
1486 restore_snapshot_handler(Path("test".to_string()), Extension(context.clone())).await;
1487 assert!(result.is_err());
1488
1489 let result = delete_snapshot_handler(Path("test".to_string()), Extension(context)).await;
1491 assert!(result.is_err());
1492 }
1493
1494 #[tokio::test]
1495 async fn test_reset_handler_success() {
1496 let (database, mut registry) = setup_test_database().await;
1497 create_test_entity(database.as_ref(), &mut registry, "User").await;
1498
1499 let context = create_test_context(database, registry);
1500
1501 let result = reset_handler(Extension(context)).await;
1502 assert!(result.is_ok());
1503 let json_value = result.unwrap().0;
1504 assert!(json_value["message"].as_str().unwrap().contains("reset successfully"));
1505 }
1506}