collapse 20 pass-through use cases; handlers call ports directly
delete get/list/list_by_owner channels, get_settings/activity_log admin, get_item/get_sync_status/list_collections/list_seasons/list_shows/list_genres library, get/list/delete providers, get/list/patch_label config_snapshots, get_active/list_history/delete_after schedule — all single-delegation. remove ChannelQueryDeps, LibraryQueryDeps, deleted query/command structs. add direct port fields to AppState. update MCP crate accordingly.
This commit is contained in:
@@ -60,10 +60,6 @@ async fn main() -> anyhow::Result<()> {
|
||||
event_publisher: event_publisher.clone(),
|
||||
});
|
||||
|
||||
let channel_query_deps = Arc::new(application::channels::ChannelQueryDeps {
|
||||
channel_query: wire.channel_query.clone(),
|
||||
});
|
||||
|
||||
let schedule_deps = Arc::new(application::schedule::ScheduleDeps {
|
||||
schedule_engine,
|
||||
channel_query: wire.channel_query.clone(),
|
||||
@@ -73,15 +69,24 @@ async fn main() -> anyhow::Result<()> {
|
||||
provider_registry: provider_registry.clone(),
|
||||
});
|
||||
|
||||
let library_query_deps = Arc::new(application::library::LibraryQueryDeps {
|
||||
let library_sync: Arc<dyn domain::ports::LibrarySyncAdapter> =
|
||||
Arc::new(NoopLibrarySync);
|
||||
|
||||
let library_command_deps = Arc::new(application::library::LibraryCommandDeps {
|
||||
library_command: wire.library_command.clone(),
|
||||
library_query: wire.library_query.clone(),
|
||||
library_sync,
|
||||
provider_registry: provider_registry.clone(),
|
||||
event_publisher: event_publisher.clone(),
|
||||
});
|
||||
|
||||
let server = KTvMcpServer {
|
||||
channel_cmd_deps,
|
||||
channel_query_deps,
|
||||
channel_query: wire.channel_query.clone(),
|
||||
schedule_deps,
|
||||
library_query_deps,
|
||||
schedule_query: wire.schedule_query.clone(),
|
||||
library_query: wire.library_query.clone(),
|
||||
library_command_deps,
|
||||
owner_id,
|
||||
};
|
||||
|
||||
@@ -103,6 +108,7 @@ struct WireOutput {
|
||||
channel_query: Arc<dyn domain::ports::ChannelQuery>,
|
||||
schedule_command: Arc<dyn domain::ports::ScheduleCommand>,
|
||||
schedule_query: Arc<dyn domain::ports::ScheduleQuery>,
|
||||
library_command: Arc<dyn domain::ports::LibraryCommand>,
|
||||
library_query: Arc<dyn domain::ports::LibraryQuery>,
|
||||
}
|
||||
|
||||
@@ -116,6 +122,7 @@ fn wire_repositories(pool: &DbPool) -> anyhow::Result<WireOutput> {
|
||||
channel_query: w.channel_query,
|
||||
schedule_command: w.schedule_command,
|
||||
schedule_query: w.schedule_query,
|
||||
library_command: w.library_command,
|
||||
library_query: w.library_query,
|
||||
})
|
||||
}
|
||||
@@ -325,3 +332,16 @@ impl IProviderRegistry for SimpleProviderRegistry {
|
||||
provider.list_genres(content_type).await
|
||||
}
|
||||
}
|
||||
|
||||
struct NoopLibrarySync;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl domain::ports::LibrarySyncAdapter for NoopLibrarySync {
|
||||
async fn sync_provider(
|
||||
&self,
|
||||
_provider: &dyn IMediaProvider,
|
||||
provider_id: &str,
|
||||
) -> domain::LibrarySyncResult {
|
||||
domain::LibrarySyncResult::with_error(provider_id, 0, "MCP does not support sync")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use application::{
|
||||
channels::{ChannelCommandDeps, ChannelQueryDeps},
|
||||
library::LibraryQueryDeps,
|
||||
channels::ChannelCommandDeps,
|
||||
library::LibraryCommandDeps,
|
||||
schedule::ScheduleDeps,
|
||||
};
|
||||
use rmcp::{
|
||||
@@ -21,9 +21,11 @@ const SERVER_NAME: &str = "k-tv-mcp";
|
||||
#[derive(Clone)]
|
||||
pub struct KTvMcpServer {
|
||||
pub channel_cmd_deps: Arc<ChannelCommandDeps>,
|
||||
pub channel_query_deps: Arc<ChannelQueryDeps>,
|
||||
pub channel_query: Arc<dyn domain::ports::ChannelQuery>,
|
||||
pub schedule_deps: Arc<ScheduleDeps>,
|
||||
pub library_query_deps: Arc<LibraryQueryDeps>,
|
||||
pub schedule_query: Arc<dyn domain::ports::ScheduleQuery>,
|
||||
pub library_query: Arc<dyn domain::ports::LibraryQuery>,
|
||||
pub library_command_deps: Arc<LibraryCommandDeps>,
|
||||
pub owner_id: Uuid,
|
||||
}
|
||||
|
||||
@@ -80,13 +82,13 @@ fn parse_uuid(s: &str) -> Result<Uuid, String> {
|
||||
impl KTvMcpServer {
|
||||
#[tool(description = "List all channels owned by the configured user")]
|
||||
async fn list_channels(&self) -> String {
|
||||
channels::list_channels(&self.channel_query_deps, self.owner_id).await
|
||||
channels::list_channels(&self.channel_query, self.owner_id).await
|
||||
}
|
||||
|
||||
#[tool(description = "Get a channel by UUID")]
|
||||
async fn get_channel(&self, #[tool(aggr)] p: GetChannelParams) -> String {
|
||||
match parse_uuid(&p.id) {
|
||||
Ok(id) => channels::get_channel(&self.channel_query_deps, id).await,
|
||||
Ok(id) => channels::get_channel(&self.channel_query, id).await,
|
||||
Err(e) => e,
|
||||
}
|
||||
}
|
||||
@@ -143,7 +145,7 @@ impl KTvMcpServer {
|
||||
#[tool(description = "Get the currently active schedule for a channel (returns null if none)")]
|
||||
async fn get_active_schedule(&self, #[tool(aggr)] p: ChannelIdParam) -> String {
|
||||
match parse_uuid(&p.channel_id) {
|
||||
Ok(id) => schedule::get_active_schedule(&self.schedule_deps, id).await,
|
||||
Ok(id) => schedule::get_active_schedule(&self.schedule_query, id).await,
|
||||
Err(e) => e,
|
||||
}
|
||||
}
|
||||
@@ -160,14 +162,14 @@ impl KTvMcpServer {
|
||||
|
||||
#[tool(description = "List media collections/libraries available in the library")]
|
||||
async fn list_collections(&self) -> String {
|
||||
library::list_collections(&self.library_query_deps).await
|
||||
library::list_collections(&self.library_query).await
|
||||
}
|
||||
|
||||
#[tool(
|
||||
description = "List genres available in the library, optionally filtered by content type (movie/episode/short)"
|
||||
)]
|
||||
async fn list_genres(&self, #[tool(aggr)] p: ListGenresParams) -> String {
|
||||
library::list_genres(&self.library_query_deps, p.content_type).await
|
||||
library::list_genres(&self.library_query, p.content_type).await
|
||||
}
|
||||
|
||||
#[tool(
|
||||
@@ -175,7 +177,7 @@ impl KTvMcpServer {
|
||||
)]
|
||||
async fn search_media(&self, #[tool(aggr)] p: SearchMediaParams) -> String {
|
||||
library::search_media(
|
||||
&self.library_query_deps,
|
||||
&self.library_command_deps,
|
||||
p.content_type,
|
||||
p.genres.unwrap_or_default(),
|
||||
p.search_term,
|
||||
|
||||
@@ -1,31 +1,27 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use application::channels::{
|
||||
ChannelCommandDeps, ChannelQueryDeps, CreateChannelCommand, DeleteChannelCommand,
|
||||
GetChannelQuery, ListByOwnerQuery, UpdateChannelCommand,
|
||||
ChannelCommandDeps, CreateChannelCommand, DeleteChannelCommand, UpdateChannelCommand,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::error::{domain_err, ok_json};
|
||||
|
||||
pub async fn list_channels(
|
||||
query_deps: &Arc<ChannelQueryDeps>,
|
||||
channel_query: &Arc<dyn domain::ports::ChannelQuery>,
|
||||
owner_id: Uuid,
|
||||
) -> String {
|
||||
let query = ListByOwnerQuery {
|
||||
owner_id: owner_id.into(),
|
||||
};
|
||||
match application::channels::list_by_owner::execute(query_deps, query).await {
|
||||
match channel_query.find_by_owner(owner_id.into()).await {
|
||||
Ok(channels) => ok_json(&channels),
|
||||
Err(e) => domain_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_channel(query_deps: &Arc<ChannelQueryDeps>, id: Uuid) -> String {
|
||||
let query = GetChannelQuery {
|
||||
channel_id: id.into(),
|
||||
};
|
||||
match application::channels::get::execute(query_deps, query).await {
|
||||
pub async fn get_channel(
|
||||
channel_query: &Arc<dyn domain::ports::ChannelQuery>,
|
||||
id: Uuid,
|
||||
) -> String {
|
||||
match channel_query.find_by_id(id.into()).await {
|
||||
Ok(Some(channel)) => ok_json(&channel),
|
||||
Ok(None) => serde_json::json!({"error": "Channel not found"}).to_string(),
|
||||
Err(e) => domain_err(e),
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use application::library::{
|
||||
LibraryQueryDeps, ListCollectionsQuery, ListGenresQuery, SearchItemsQuery,
|
||||
};
|
||||
use application::library::SearchItemsQuery;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::error::{domain_err, ok_json};
|
||||
@@ -48,9 +46,8 @@ fn content_type_to_str(ct: &domain::ContentType) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn list_collections(deps: &Arc<LibraryQueryDeps>) -> String {
|
||||
let query = ListCollectionsQuery { provider_id: None };
|
||||
match application::library::list_collections::execute(deps, query).await {
|
||||
pub async fn list_collections(library_query: &Arc<dyn domain::ports::LibraryQuery>) -> String {
|
||||
match library_query.list_collections(None).await {
|
||||
Ok(cols) => {
|
||||
let dtos: Vec<CollectionDto> = cols
|
||||
.into_iter()
|
||||
@@ -66,19 +63,26 @@ pub async fn list_collections(deps: &Arc<LibraryQueryDeps>) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn list_genres(deps: &Arc<LibraryQueryDeps>, content_type: Option<String>) -> String {
|
||||
let query = ListGenresQuery {
|
||||
content_type,
|
||||
provider_id: None,
|
||||
pub async fn list_genres(
|
||||
library_query: &Arc<dyn domain::ports::LibraryQuery>,
|
||||
content_type: Option<String>,
|
||||
) -> String {
|
||||
let ct = match content_type
|
||||
.as_deref()
|
||||
.map(application::library::parse_content_type)
|
||||
.transpose()
|
||||
{
|
||||
Ok(ct) => ct,
|
||||
Err(e) => return domain_err(e),
|
||||
};
|
||||
match application::library::list_genres::execute(deps, query).await {
|
||||
match library_query.list_genres(ct.as_ref(), None).await {
|
||||
Ok(genres) => ok_json(&genres),
|
||||
Err(e) => domain_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn search_media(
|
||||
deps: &Arc<LibraryQueryDeps>,
|
||||
library_command_deps: &Arc<application::library::LibraryCommandDeps>,
|
||||
content_type: Option<String>,
|
||||
genres: Vec<String>,
|
||||
search_term: Option<String>,
|
||||
@@ -97,7 +101,7 @@ pub async fn search_media(
|
||||
offset: 0,
|
||||
limit: DEFAULT_SEARCH_LIMIT,
|
||||
};
|
||||
match application::library::search::execute(deps, query).await {
|
||||
match application::library::search::execute(library_command_deps, query).await {
|
||||
Ok((items, total)) => {
|
||||
let dtos: Vec<LibraryItemDto> = items
|
||||
.into_iter()
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use application::schedule::{
|
||||
GenerateScheduleCommand, GetActiveScheduleQuery, GetCurrentBroadcastQuery, ScheduleDeps,
|
||||
GenerateScheduleCommand, GetCurrentBroadcastQuery, ScheduleDeps,
|
||||
};
|
||||
use chrono::Utc;
|
||||
use domain::ScheduledSlot;
|
||||
use domain::value_objects::ChannelId;
|
||||
use serde::Serialize;
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -23,9 +25,12 @@ pub async fn generate_schedule(deps: &Arc<ScheduleDeps>, channel_id: Uuid) -> St
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_active_schedule(deps: &Arc<ScheduleDeps>, channel_id: Uuid) -> String {
|
||||
let query = GetActiveScheduleQuery { channel_id };
|
||||
match application::schedule::get_active::execute(deps, query).await {
|
||||
pub async fn get_active_schedule(
|
||||
schedule_query: &Arc<dyn domain::ports::ScheduleQuery>,
|
||||
channel_id: Uuid,
|
||||
) -> String {
|
||||
let cid = ChannelId::from(channel_id);
|
||||
match schedule_query.find_active(cid, Utc::now()).await {
|
||||
Ok(Some(schedule)) => ok_json(&schedule),
|
||||
Ok(None) => "null".to_string(),
|
||||
Err(e) => domain_err(e),
|
||||
|
||||
Reference in New Issue
Block a user