use crate::core::query::QueryParams;
use crate::links::handlers::{
AppState, create_link, create_linked_entity, delete_link, get_link, get_link_by_route,
handle_nested_path_get, list_available_links, list_links, update_link,
};
use axum::{Router, extract::Query, routing::get};
#[cfg(feature = "grpc")]
pub fn combine_rest_and_grpc(rest_router: Router, grpc_router: Router) -> Router {
rest_router.merge(grpc_router)
}
pub fn build_link_routes(state: AppState) -> Router {
use axum::extract::{Path as AxumPath, Request, State as AxumState};
use axum::response::IntoResponse;
use uuid::Uuid;
let smart_handler = |AxumState(state): AxumState<AppState>,
AxumPath((entity_type_plural, entity_id, route_name)): AxumPath<(
String,
Uuid,
String,
)>,
Query(params): Query<QueryParams>,
req: Request| async move {
let path = req.uri().path();
let segments: Vec<&str> = path.trim_matches('/').split('/').collect();
if segments.len() >= 5 {
handle_nested_path_get(AxumState(state), AxumPath(path.to_string()), Query(params))
.await
.map(|r| r.into_response())
} else {
list_links(
AxumState(state),
AxumPath((entity_type_plural, entity_id, route_name)),
Query(params),
)
.await
.map(|r| r.into_response())
}
};
let fallback_handler = |AxumState(state): AxumState<AppState>,
Query(params): Query<QueryParams>,
req: Request| async move {
let path = req.uri().path().to_string();
handle_nested_path_get(AxumState(state), AxumPath(path), Query(params))
.await
.map(|r| r.into_response())
};
Router::new()
.route("/links/{link_id}", get(get_link))
.route(
"/{entity_type}/{entity_id}/{route_name}",
get(smart_handler).post(create_linked_entity),
)
.route(
"/{source_type}/{source_id}/{route_name}/{target_id}",
get(get_link_by_route)
.post(create_link)
.put(update_link)
.delete(delete_link),
)
.route(
"/{entity_type}/{entity_id}/links",
get(list_available_links),
)
.fallback(fallback_handler)
.with_state(state)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::LinksConfig;
use crate::core::events::EventBus;
use crate::links::handlers::AppState;
use crate::links::registry::LinkRouteRegistry;
use crate::storage::InMemoryLinkService;
use std::collections::HashMap;
use std::sync::Arc;
fn test_app_state() -> AppState {
let config = Arc::new(LinksConfig::default_config());
let registry = Arc::new(LinkRouteRegistry::new(config.clone()));
AppState {
link_service: Arc::new(InMemoryLinkService::new()),
config,
registry,
entity_fetchers: Arc::new(HashMap::new()),
entity_creators: Arc::new(HashMap::new()),
event_bus: None,
}
}
#[test]
fn test_build_link_routes_produces_router() {
let state = test_app_state();
let router = build_link_routes(state);
let _ = router;
}
#[test]
fn test_build_link_routes_with_event_bus() {
let config = Arc::new(LinksConfig::default_config());
let registry = Arc::new(LinkRouteRegistry::new(config.clone()));
let state = AppState {
link_service: Arc::new(InMemoryLinkService::new()),
config,
registry,
entity_fetchers: Arc::new(HashMap::new()),
entity_creators: Arc::new(HashMap::new()),
event_bus: Some(Arc::new(EventBus::new(16))),
};
let router = build_link_routes(state);
let _ = router;
}
#[test]
fn test_build_link_routes_empty_config() {
let config = Arc::new(LinksConfig {
entities: vec![],
links: vec![],
validation_rules: None,
events: None,
sinks: None,
});
let registry = Arc::new(LinkRouteRegistry::new(config.clone()));
let state = AppState {
link_service: Arc::new(InMemoryLinkService::new()),
config,
registry,
entity_fetchers: Arc::new(HashMap::new()),
entity_creators: Arc::new(HashMap::new()),
event_bus: None,
};
let router = build_link_routes(state);
let _ = router;
}
#[cfg(feature = "grpc")]
mod grpc_tests {
use super::super::combine_rest_and_grpc;
use axum::Router;
#[test]
fn test_combine_rest_and_grpc_merges_routers() {
let rest = Router::new();
let grpc = Router::new();
let combined = combine_rest_and_grpc(rest, grpc);
let _ = combined;
}
}
}