feat(bd-2g50): fill data gaps in issue detail view
Add references_full, user_notes_count, merge_requests_count computed fields to show issue. Add closed_at and confidential columns via migration 023. Closes: bd-2g50
This commit is contained in:
5
migrations/023_issue_detail_fields.sql
Normal file
5
migrations/023_issue_detail_fields.sql
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
ALTER TABLE issues ADD COLUMN closed_at TEXT;
|
||||||
|
ALTER TABLE issues ADD COLUMN confidential INTEGER NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
INSERT INTO schema_version (version, applied_at, description)
|
||||||
|
VALUES (23, strftime('%s', 'now') * 1000, 'Add closed_at and confidential to issues');
|
||||||
@@ -75,12 +75,17 @@ pub struct IssueDetail {
|
|||||||
pub author_username: String,
|
pub author_username: String,
|
||||||
pub created_at: i64,
|
pub created_at: i64,
|
||||||
pub updated_at: i64,
|
pub updated_at: i64,
|
||||||
|
pub closed_at: Option<String>,
|
||||||
|
pub confidential: bool,
|
||||||
pub web_url: Option<String>,
|
pub web_url: Option<String>,
|
||||||
pub project_path: String,
|
pub project_path: String,
|
||||||
|
pub references_full: String,
|
||||||
pub labels: Vec<String>,
|
pub labels: Vec<String>,
|
||||||
pub assignees: Vec<String>,
|
pub assignees: Vec<String>,
|
||||||
pub due_date: Option<String>,
|
pub due_date: Option<String>,
|
||||||
pub milestone: Option<String>,
|
pub milestone: Option<String>,
|
||||||
|
pub user_notes_count: i64,
|
||||||
|
pub merge_requests_count: usize,
|
||||||
pub closing_merge_requests: Vec<ClosingMrRef>,
|
pub closing_merge_requests: Vec<ClosingMrRef>,
|
||||||
pub discussions: Vec<DiscussionDetail>,
|
pub discussions: Vec<DiscussionDetail>,
|
||||||
pub status_name: Option<String>,
|
pub status_name: Option<String>,
|
||||||
@@ -122,6 +127,9 @@ pub fn run_show_issue(
|
|||||||
|
|
||||||
let discussions = get_issue_discussions(&conn, issue.id)?;
|
let discussions = get_issue_discussions(&conn, issue.id)?;
|
||||||
|
|
||||||
|
let references_full = format!("{}#{}", issue.project_path, issue.iid);
|
||||||
|
let merge_requests_count = closing_mrs.len();
|
||||||
|
|
||||||
Ok(IssueDetail {
|
Ok(IssueDetail {
|
||||||
id: issue.id,
|
id: issue.id,
|
||||||
iid: issue.iid,
|
iid: issue.iid,
|
||||||
@@ -131,12 +139,17 @@ pub fn run_show_issue(
|
|||||||
author_username: issue.author_username,
|
author_username: issue.author_username,
|
||||||
created_at: issue.created_at,
|
created_at: issue.created_at,
|
||||||
updated_at: issue.updated_at,
|
updated_at: issue.updated_at,
|
||||||
|
closed_at: issue.closed_at,
|
||||||
|
confidential: issue.confidential,
|
||||||
web_url: issue.web_url,
|
web_url: issue.web_url,
|
||||||
project_path: issue.project_path,
|
project_path: issue.project_path,
|
||||||
|
references_full,
|
||||||
labels,
|
labels,
|
||||||
assignees,
|
assignees,
|
||||||
due_date: issue.due_date,
|
due_date: issue.due_date,
|
||||||
milestone: issue.milestone_title,
|
milestone: issue.milestone_title,
|
||||||
|
user_notes_count: issue.user_notes_count,
|
||||||
|
merge_requests_count,
|
||||||
closing_merge_requests: closing_mrs,
|
closing_merge_requests: closing_mrs,
|
||||||
discussions,
|
discussions,
|
||||||
status_name: issue.status_name,
|
status_name: issue.status_name,
|
||||||
@@ -156,10 +169,13 @@ struct IssueRow {
|
|||||||
author_username: String,
|
author_username: String,
|
||||||
created_at: i64,
|
created_at: i64,
|
||||||
updated_at: i64,
|
updated_at: i64,
|
||||||
|
closed_at: Option<String>,
|
||||||
|
confidential: bool,
|
||||||
web_url: Option<String>,
|
web_url: Option<String>,
|
||||||
project_path: String,
|
project_path: String,
|
||||||
due_date: Option<String>,
|
due_date: Option<String>,
|
||||||
milestone_title: Option<String>,
|
milestone_title: Option<String>,
|
||||||
|
user_notes_count: i64,
|
||||||
status_name: Option<String>,
|
status_name: Option<String>,
|
||||||
status_category: Option<String>,
|
status_category: Option<String>,
|
||||||
status_color: Option<String>,
|
status_color: Option<String>,
|
||||||
@@ -173,8 +189,12 @@ fn find_issue(conn: &Connection, iid: i64, project_filter: Option<&str>) -> Resu
|
|||||||
let project_id = resolve_project(conn, project)?;
|
let project_id = resolve_project(conn, project)?;
|
||||||
(
|
(
|
||||||
"SELECT i.id, i.iid, i.title, i.description, i.state, i.author_username,
|
"SELECT i.id, i.iid, i.title, i.description, i.state, i.author_username,
|
||||||
i.created_at, i.updated_at, i.web_url, p.path_with_namespace,
|
i.created_at, i.updated_at, i.closed_at, i.confidential,
|
||||||
|
i.web_url, p.path_with_namespace,
|
||||||
i.due_date, i.milestone_title,
|
i.due_date, i.milestone_title,
|
||||||
|
(SELECT COUNT(*) FROM notes n
|
||||||
|
JOIN discussions d ON n.discussion_id = d.id
|
||||||
|
WHERE d.noteable_type = 'Issue' AND d.noteable_id = i.id AND n.is_system = 0) AS user_notes_count,
|
||||||
i.status_name, i.status_category, i.status_color,
|
i.status_name, i.status_category, i.status_color,
|
||||||
i.status_icon_name, i.status_synced_at
|
i.status_icon_name, i.status_synced_at
|
||||||
FROM issues i
|
FROM issues i
|
||||||
@@ -185,8 +205,12 @@ fn find_issue(conn: &Connection, iid: i64, project_filter: Option<&str>) -> Resu
|
|||||||
}
|
}
|
||||||
None => (
|
None => (
|
||||||
"SELECT i.id, i.iid, i.title, i.description, i.state, i.author_username,
|
"SELECT i.id, i.iid, i.title, i.description, i.state, i.author_username,
|
||||||
i.created_at, i.updated_at, i.web_url, p.path_with_namespace,
|
i.created_at, i.updated_at, i.closed_at, i.confidential,
|
||||||
|
i.web_url, p.path_with_namespace,
|
||||||
i.due_date, i.milestone_title,
|
i.due_date, i.milestone_title,
|
||||||
|
(SELECT COUNT(*) FROM notes n
|
||||||
|
JOIN discussions d ON n.discussion_id = d.id
|
||||||
|
WHERE d.noteable_type = 'Issue' AND d.noteable_id = i.id AND n.is_system = 0) AS user_notes_count,
|
||||||
i.status_name, i.status_category, i.status_color,
|
i.status_name, i.status_category, i.status_color,
|
||||||
i.status_icon_name, i.status_synced_at
|
i.status_icon_name, i.status_synced_at
|
||||||
FROM issues i
|
FROM issues i
|
||||||
@@ -201,6 +225,7 @@ fn find_issue(conn: &Connection, iid: i64, project_filter: Option<&str>) -> Resu
|
|||||||
let mut stmt = conn.prepare(sql)?;
|
let mut stmt = conn.prepare(sql)?;
|
||||||
let issues: Vec<IssueRow> = stmt
|
let issues: Vec<IssueRow> = stmt
|
||||||
.query_map(param_refs.as_slice(), |row| {
|
.query_map(param_refs.as_slice(), |row| {
|
||||||
|
let confidential_val: i64 = row.get(9)?;
|
||||||
Ok(IssueRow {
|
Ok(IssueRow {
|
||||||
id: row.get(0)?,
|
id: row.get(0)?,
|
||||||
iid: row.get(1)?,
|
iid: row.get(1)?,
|
||||||
@@ -210,15 +235,18 @@ fn find_issue(conn: &Connection, iid: i64, project_filter: Option<&str>) -> Resu
|
|||||||
author_username: row.get(5)?,
|
author_username: row.get(5)?,
|
||||||
created_at: row.get(6)?,
|
created_at: row.get(6)?,
|
||||||
updated_at: row.get(7)?,
|
updated_at: row.get(7)?,
|
||||||
web_url: row.get(8)?,
|
closed_at: row.get(8)?,
|
||||||
project_path: row.get(9)?,
|
confidential: confidential_val != 0,
|
||||||
due_date: row.get(10)?,
|
web_url: row.get(10)?,
|
||||||
milestone_title: row.get(11)?,
|
project_path: row.get(11)?,
|
||||||
status_name: row.get(12)?,
|
due_date: row.get(12)?,
|
||||||
status_category: row.get(13)?,
|
milestone_title: row.get(13)?,
|
||||||
status_color: row.get(14)?,
|
user_notes_count: row.get(14)?,
|
||||||
status_icon_name: row.get(15)?,
|
status_name: row.get(15)?,
|
||||||
status_synced_at: row.get(16)?,
|
status_category: row.get(16)?,
|
||||||
|
status_color: row.get(17)?,
|
||||||
|
status_icon_name: row.get(18)?,
|
||||||
|
status_synced_at: row.get(19)?,
|
||||||
})
|
})
|
||||||
})?
|
})?
|
||||||
.collect::<std::result::Result<Vec<_>, _>>()?;
|
.collect::<std::result::Result<Vec<_>, _>>()?;
|
||||||
@@ -618,6 +646,7 @@ pub fn print_show_issue(issue: &IssueDetail) {
|
|||||||
println!("{}", "━".repeat(header.len().min(80)));
|
println!("{}", "━".repeat(header.len().min(80)));
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
|
println!("Ref: {}", style(&issue.references_full).dim());
|
||||||
println!("Project: {}", style(&issue.project_path).cyan());
|
println!("Project: {}", style(&issue.project_path).cyan());
|
||||||
|
|
||||||
let state_styled = if issue.state == "opened" {
|
let state_styled = if issue.state == "opened" {
|
||||||
@@ -627,6 +656,10 @@ pub fn print_show_issue(issue: &IssueDetail) {
|
|||||||
};
|
};
|
||||||
println!("State: {}", state_styled);
|
println!("State: {}", state_styled);
|
||||||
|
|
||||||
|
if issue.confidential {
|
||||||
|
println!(" {}", style("CONFIDENTIAL").red().bold());
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(status) = &issue.status_name {
|
if let Some(status) = &issue.status_name {
|
||||||
println!(
|
println!(
|
||||||
"Status: {}",
|
"Status: {}",
|
||||||
@@ -658,6 +691,10 @@ pub fn print_show_issue(issue: &IssueDetail) {
|
|||||||
println!("Created: {}", format_date(issue.created_at));
|
println!("Created: {}", format_date(issue.created_at));
|
||||||
println!("Updated: {}", format_date(issue.updated_at));
|
println!("Updated: {}", format_date(issue.updated_at));
|
||||||
|
|
||||||
|
if let Some(closed_at) = &issue.closed_at {
|
||||||
|
println!("Closed: {}", closed_at);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(due) = &issue.due_date {
|
if let Some(due) = &issue.due_date {
|
||||||
println!("Due: {}", due);
|
println!("Due: {}", due);
|
||||||
}
|
}
|
||||||
@@ -931,12 +968,17 @@ pub struct IssueDetailJson {
|
|||||||
pub author_username: String,
|
pub author_username: String,
|
||||||
pub created_at: String,
|
pub created_at: String,
|
||||||
pub updated_at: String,
|
pub updated_at: String,
|
||||||
|
pub closed_at: Option<String>,
|
||||||
|
pub confidential: bool,
|
||||||
pub web_url: Option<String>,
|
pub web_url: Option<String>,
|
||||||
pub project_path: String,
|
pub project_path: String,
|
||||||
|
pub references_full: String,
|
||||||
pub labels: Vec<String>,
|
pub labels: Vec<String>,
|
||||||
pub assignees: Vec<String>,
|
pub assignees: Vec<String>,
|
||||||
pub due_date: Option<String>,
|
pub due_date: Option<String>,
|
||||||
pub milestone: Option<String>,
|
pub milestone: Option<String>,
|
||||||
|
pub user_notes_count: i64,
|
||||||
|
pub merge_requests_count: usize,
|
||||||
pub closing_merge_requests: Vec<ClosingMrRefJson>,
|
pub closing_merge_requests: Vec<ClosingMrRefJson>,
|
||||||
pub discussions: Vec<DiscussionDetailJson>,
|
pub discussions: Vec<DiscussionDetailJson>,
|
||||||
pub status_name: Option<String>,
|
pub status_name: Option<String>,
|
||||||
@@ -980,12 +1022,17 @@ impl From<&IssueDetail> for IssueDetailJson {
|
|||||||
author_username: issue.author_username.clone(),
|
author_username: issue.author_username.clone(),
|
||||||
created_at: ms_to_iso(issue.created_at),
|
created_at: ms_to_iso(issue.created_at),
|
||||||
updated_at: ms_to_iso(issue.updated_at),
|
updated_at: ms_to_iso(issue.updated_at),
|
||||||
|
closed_at: issue.closed_at.clone(),
|
||||||
|
confidential: issue.confidential,
|
||||||
web_url: issue.web_url.clone(),
|
web_url: issue.web_url.clone(),
|
||||||
project_path: issue.project_path.clone(),
|
project_path: issue.project_path.clone(),
|
||||||
|
references_full: issue.references_full.clone(),
|
||||||
labels: issue.labels.clone(),
|
labels: issue.labels.clone(),
|
||||||
assignees: issue.assignees.clone(),
|
assignees: issue.assignees.clone(),
|
||||||
due_date: issue.due_date.clone(),
|
due_date: issue.due_date.clone(),
|
||||||
milestone: issue.milestone.clone(),
|
milestone: issue.milestone.clone(),
|
||||||
|
user_notes_count: issue.user_notes_count,
|
||||||
|
merge_requests_count: issue.merge_requests_count,
|
||||||
closing_merge_requests: issue
|
closing_merge_requests: issue
|
||||||
.closing_merge_requests
|
.closing_merge_requests
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@@ -69,6 +69,10 @@ const MIGRATIONS: &[(&str, &str)] = &[
|
|||||||
"021",
|
"021",
|
||||||
include_str!("../../migrations/021_work_item_status.sql"),
|
include_str!("../../migrations/021_work_item_status.sql"),
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"023",
|
||||||
|
include_str!("../../migrations/023_issue_detail_fields.sql"),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn create_connection(db_path: &Path) -> Result<Connection> {
|
pub fn create_connection(db_path: &Path) -> Result<Connection> {
|
||||||
|
|||||||
Reference in New Issue
Block a user