|
|
|
|
@ -11,9 +11,10 @@ use serde_json::{json, Value};
|
|
|
|
|
use tauri::State;
|
|
|
|
|
|
|
|
|
|
use application::{
|
|
|
|
|
AppError, AssignIssueAgentInput, AssignTicketToSprintInput, CloseTicketAssistantInput,
|
|
|
|
|
CreateSprintInput, DeleteIssueInput, DeleteSprintInput, LinkIssuesInput, ListIssuesInput,
|
|
|
|
|
ListSprintsInput, OpenProjectInput, OpenTicketAssistantInput, ReadIssueCarnetInput,
|
|
|
|
|
AddIssueAttachmentInput, AppError, AssignIssueAgentInput, AssignTicketToSprintInput,
|
|
|
|
|
CloseTicketAssistantInput, CreateSprintInput, DeleteIssueInput, DeleteSprintInput,
|
|
|
|
|
LinkIssuesInput, ListIssuesInput, ListSprintsInput, MarkIssueAttachmentSummarizedInput,
|
|
|
|
|
OpenProjectInput, OpenTicketAssistantInput, ReadIssueAttachmentInput, ReadIssueCarnetInput,
|
|
|
|
|
ReadIssueInput, RenameSprintInput, ReorderSprintsInput, UnassignTicketFromSprintInput,
|
|
|
|
|
UnlinkIssuesInput, UpdateIssueCarnetInput, UpdateIssueInput,
|
|
|
|
|
};
|
|
|
|
|
@ -34,6 +35,9 @@ pub struct AppTicketToolProvider {
|
|
|
|
|
pub bulk_delete: Arc<application::BulkDeleteIssues>,
|
|
|
|
|
pub read_carnet: Arc<application::ReadIssueCarnet>,
|
|
|
|
|
pub update_carnet: Arc<application::UpdateIssueCarnet>,
|
|
|
|
|
pub add_attachment: Arc<application::AddIssueAttachment>,
|
|
|
|
|
pub read_attachment: Arc<application::ReadIssueAttachment>,
|
|
|
|
|
pub mark_attachment_summarized: Arc<application::MarkIssueAttachmentSummarized>,
|
|
|
|
|
pub link: Arc<application::LinkIssues>,
|
|
|
|
|
pub unlink: Arc<application::UnlinkIssues>,
|
|
|
|
|
pub list_sprints: Arc<application::ListSprints>,
|
|
|
|
|
@ -251,6 +255,57 @@ impl TicketToolProvider for AppTicketToolProvider {
|
|
|
|
|
Some(read_carnet_body(&*self.read_carnet, project, issue_ref).await?)
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
"idea_ticket_attachment_add" => {
|
|
|
|
|
let req = mcp_attachment_add_request(project, arguments)?;
|
|
|
|
|
let issue = self
|
|
|
|
|
.add_attachment
|
|
|
|
|
.execute(AddIssueAttachmentInput {
|
|
|
|
|
project: project.clone(),
|
|
|
|
|
issue_ref: parse_ref_dto(&req.r#ref).map_err(dto_tool_error)?,
|
|
|
|
|
path: req.path,
|
|
|
|
|
mime: req.mime,
|
|
|
|
|
expected_version: domain::IssueVersion::new(req.expected_version)
|
|
|
|
|
.map_err(|e| TicketToolError::new("invalid", e.to_string()))?,
|
|
|
|
|
actor,
|
|
|
|
|
})
|
|
|
|
|
.await
|
|
|
|
|
.map_err(ticket_error)?
|
|
|
|
|
.issue;
|
|
|
|
|
json!(TicketDto::from_issue(issue, None))
|
|
|
|
|
}
|
|
|
|
|
"idea_ticket_attachment_read" => {
|
|
|
|
|
let req = mcp_attachment_read_request(project, arguments)?;
|
|
|
|
|
let content = self
|
|
|
|
|
.read_attachment
|
|
|
|
|
.execute(ReadIssueAttachmentInput {
|
|
|
|
|
project: project.clone(),
|
|
|
|
|
issue_ref: parse_ref_dto(&req.r#ref).map_err(dto_tool_error)?,
|
|
|
|
|
attachment_id: parse_attachment_id_dto(&req.attachment_id)
|
|
|
|
|
.map_err(dto_tool_error)?,
|
|
|
|
|
})
|
|
|
|
|
.await
|
|
|
|
|
.map_err(ticket_error)?
|
|
|
|
|
.content;
|
|
|
|
|
json!(TicketAttachmentContentDto::from(content))
|
|
|
|
|
}
|
|
|
|
|
"idea_ticket_attachment_mark_summarized" => {
|
|
|
|
|
let req = mcp_attachment_mark_summarized_request(project, arguments)?;
|
|
|
|
|
let issue = self
|
|
|
|
|
.mark_attachment_summarized
|
|
|
|
|
.execute(MarkIssueAttachmentSummarizedInput {
|
|
|
|
|
project: project.clone(),
|
|
|
|
|
issue_ref: parse_ref_dto(&req.r#ref).map_err(dto_tool_error)?,
|
|
|
|
|
attachment_id: parse_attachment_id_dto(&req.attachment_id)
|
|
|
|
|
.map_err(dto_tool_error)?,
|
|
|
|
|
expected_version: domain::IssueVersion::new(req.expected_version)
|
|
|
|
|
.map_err(|e| TicketToolError::new("invalid", e.to_string()))?,
|
|
|
|
|
actor,
|
|
|
|
|
})
|
|
|
|
|
.await
|
|
|
|
|
.map_err(ticket_error)?
|
|
|
|
|
.issue;
|
|
|
|
|
json!(TicketDto::from_issue(issue, None))
|
|
|
|
|
}
|
|
|
|
|
"idea_ticket_link" => {
|
|
|
|
|
let issue = self
|
|
|
|
|
.link
|
|
|
|
|
@ -348,6 +403,78 @@ pub async fn ticket_read(
|
|
|
|
|
Ok(TicketDto::from_issue(issue, carnet))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
|
|
|
|
pub async fn ticket_attachment_add(
|
|
|
|
|
request: TicketAttachmentAddRequestDto,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<TicketDto, ErrorDto> {
|
|
|
|
|
let project = resolve_project(&state, &request.project_id).await?;
|
|
|
|
|
let issue = state
|
|
|
|
|
.add_issue_attachment
|
|
|
|
|
.execute(AddIssueAttachmentInput {
|
|
|
|
|
project,
|
|
|
|
|
issue_ref: parse_ref_dto(&request.r#ref)?,
|
|
|
|
|
path: request.path,
|
|
|
|
|
mime: request.mime,
|
|
|
|
|
expected_version: domain::IssueVersion::new(request.expected_version).map_err(|e| {
|
|
|
|
|
ErrorDto {
|
|
|
|
|
code: "INVALID".to_owned(),
|
|
|
|
|
message: e.to_string(),
|
|
|
|
|
}
|
|
|
|
|
})?,
|
|
|
|
|
actor: IssueActor::User,
|
|
|
|
|
})
|
|
|
|
|
.await
|
|
|
|
|
.map_err(ErrorDto::from)?
|
|
|
|
|
.issue;
|
|
|
|
|
Ok(TicketDto::from_issue(issue, None))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
|
|
|
|
pub async fn ticket_attachment_read(
|
|
|
|
|
request: TicketAttachmentReadRequestDto,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<TicketAttachmentContentDto, ErrorDto> {
|
|
|
|
|
let project = resolve_project(&state, &request.project_id).await?;
|
|
|
|
|
let content = state
|
|
|
|
|
.read_issue_attachment
|
|
|
|
|
.execute(ReadIssueAttachmentInput {
|
|
|
|
|
project,
|
|
|
|
|
issue_ref: parse_ref_dto(&request.r#ref)?,
|
|
|
|
|
attachment_id: parse_attachment_id_dto(&request.attachment_id)?,
|
|
|
|
|
})
|
|
|
|
|
.await
|
|
|
|
|
.map_err(ErrorDto::from)?
|
|
|
|
|
.content;
|
|
|
|
|
Ok(TicketAttachmentContentDto::from(content))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
|
|
|
|
pub async fn ticket_attachment_mark_summarized(
|
|
|
|
|
request: TicketAttachmentMarkSummarizedRequestDto,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<TicketDto, ErrorDto> {
|
|
|
|
|
let project = resolve_project(&state, &request.project_id).await?;
|
|
|
|
|
let issue = state
|
|
|
|
|
.mark_issue_attachment_summarized
|
|
|
|
|
.execute(MarkIssueAttachmentSummarizedInput {
|
|
|
|
|
project,
|
|
|
|
|
issue_ref: parse_ref_dto(&request.r#ref)?,
|
|
|
|
|
attachment_id: parse_attachment_id_dto(&request.attachment_id)?,
|
|
|
|
|
expected_version: domain::IssueVersion::new(request.expected_version).map_err(|e| {
|
|
|
|
|
ErrorDto {
|
|
|
|
|
code: "INVALID".to_owned(),
|
|
|
|
|
message: e.to_string(),
|
|
|
|
|
}
|
|
|
|
|
})?,
|
|
|
|
|
actor: IssueActor::User,
|
|
|
|
|
})
|
|
|
|
|
.await
|
|
|
|
|
.map_err(ErrorDto::from)?
|
|
|
|
|
.issue;
|
|
|
|
|
Ok(TicketDto::from_issue(issue, None))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
|
|
|
|
pub async fn ticket_delete(
|
|
|
|
|
request: TicketDeleteRequestDto,
|
|
|
|
|
@ -876,6 +1003,36 @@ fn mcp_bulk_delete_request(
|
|
|
|
|
Ok(req)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn mcp_attachment_add_request(
|
|
|
|
|
project: &Project,
|
|
|
|
|
arguments: Value,
|
|
|
|
|
) -> Result<TicketAttachmentAddRequestDto, TicketToolError> {
|
|
|
|
|
let mut req: TicketAttachmentAddRequestDto = serde_json::from_value(arguments)
|
|
|
|
|
.map_err(|e| TicketToolError::new("invalid", e.to_string()))?;
|
|
|
|
|
req.project_id = project.id.to_string();
|
|
|
|
|
Ok(req)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn mcp_attachment_read_request(
|
|
|
|
|
project: &Project,
|
|
|
|
|
arguments: Value,
|
|
|
|
|
) -> Result<TicketAttachmentReadRequestDto, TicketToolError> {
|
|
|
|
|
let mut req: TicketAttachmentReadRequestDto = serde_json::from_value(arguments)
|
|
|
|
|
.map_err(|e| TicketToolError::new("invalid", e.to_string()))?;
|
|
|
|
|
req.project_id = project.id.to_string();
|
|
|
|
|
Ok(req)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn mcp_attachment_mark_summarized_request(
|
|
|
|
|
project: &Project,
|
|
|
|
|
arguments: Value,
|
|
|
|
|
) -> Result<TicketAttachmentMarkSummarizedRequestDto, TicketToolError> {
|
|
|
|
|
let mut req: TicketAttachmentMarkSummarizedRequestDto = serde_json::from_value(arguments)
|
|
|
|
|
.map_err(|e| TicketToolError::new("invalid", e.to_string()))?;
|
|
|
|
|
req.project_id = project.id.to_string();
|
|
|
|
|
Ok(req)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_json_ref(arguments: &Value, key: &str) -> Result<IssueRef, TicketToolError> {
|
|
|
|
|
required_str(arguments, key).and_then(|raw| parse_ref_dto(raw).map_err(dto_tool_error))
|
|
|
|
|
}
|
|
|
|
|
|