feat(forge_notify): assigned-issues rollup todo
After each notification poll, query GET /api/v1/issues/search with assigned=true for both issues and pulls, read X-Total-Count, and upsert/clear a keyed 'rollup' todo (subsystem=forge, key=rollup). When the total is > 0 the summary reads e.g. '3 open assigned: 2 issues, 1 PR'. When it drops to 0 the rollup todo is cleared. The 'rollup' key is distinct from per-thread numeric keys so clearing it never touches notification todos. Closes #2725.
This commit is contained in:
parent
7ba90d5a3f
commit
7320e2ba3d
1 changed files with 80 additions and 0 deletions
|
|
@ -186,6 +186,7 @@ pub async fn run(socket: PathBuf) {
|
|||
}
|
||||
}
|
||||
poll_once(&forge, &client, &token, &socket, &mut delivered, &own_login).await;
|
||||
update_assigned_rollup(&client, &forge_url, &token, &socket).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1170,6 +1171,85 @@ async fn mark_read(forge: &Forgejo, id: u64) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Fetch the total count of open issues or PRs assigned to this agent via
|
||||
/// Forgejo's global search API (`GET /api/v1/issues/search`). `issue_type`
|
||||
/// is `"issues"` or `"pulls"`. Reads the `X-Total-Count` response header
|
||||
/// rather than deserialising the full body — only page 1 with limit 1 is
|
||||
/// fetched, keeping the request cheap. Returns `None` on any error (timeout,
|
||||
/// HTTP error, missing or unparseable header).
|
||||
async fn count_assigned(
|
||||
client: &reqwest::Client,
|
||||
forge_url: &str,
|
||||
token: &str,
|
||||
issue_type: &str,
|
||||
) -> Option<u64> {
|
||||
let url = format!(
|
||||
"{forge_url}/api/v1/issues/search\
|
||||
?type={issue_type}&state=open&assigned=true&limit=1&page=1"
|
||||
);
|
||||
let resp = match client
|
||||
.get(&url)
|
||||
.header("Authorization", format!("token {token}"))
|
||||
.send()
|
||||
.await
|
||||
{
|
||||
Ok(r) if r.status().is_success() => r,
|
||||
_ => return None,
|
||||
};
|
||||
let count_str = resp.headers().get("x-total-count")?.to_str().ok()?;
|
||||
count_str.parse::<u64>().ok()
|
||||
}
|
||||
|
||||
/// After each notification poll, query the forge for the count of open
|
||||
/// issues and PRs assigned to this agent and keep a keyed `"rollup"` todo
|
||||
/// in sync. When the count is positive the todo summarises the breakdown
|
||||
/// (`N issues, M PRs`); when it reaches zero the todo is cleared. The rollup
|
||||
/// key is distinct from per-thread numeric keys so clearing it never touches
|
||||
/// notification todos.
|
||||
async fn update_assigned_rollup(
|
||||
client: &reqwest::Client,
|
||||
forge_url: &str,
|
||||
token: &str,
|
||||
socket: &Path,
|
||||
) {
|
||||
let issues = count_assigned(client, forge_url, token, "issues")
|
||||
.await
|
||||
.unwrap_or(0);
|
||||
let pulls = count_assigned(client, forge_url, token, "pulls")
|
||||
.await
|
||||
.unwrap_or(0);
|
||||
let total = issues + pulls;
|
||||
|
||||
let req = if total == 0 {
|
||||
hive_agent_sock::Request::ClearTodo {
|
||||
subsystem: "forge".to_owned(),
|
||||
key: Some("rollup".to_owned()),
|
||||
all: false,
|
||||
}
|
||||
} else {
|
||||
let breakdown = match (issues, pulls) {
|
||||
(i, 0) => format!("{i} issue{}", if i == 1 { "" } else { "s" }),
|
||||
(0, p) => format!("{p} PR{}", if p == 1 { "" } else { "s" }),
|
||||
(i, p) => format!(
|
||||
"{i} issue{}, {p} PR{}",
|
||||
if i == 1 { "" } else { "s" },
|
||||
if p == 1 { "" } else { "s" }
|
||||
),
|
||||
};
|
||||
hive_agent_sock::Request::UpsertTodo {
|
||||
subsystem: "forge".to_owned(),
|
||||
key: Some("rollup".to_owned()),
|
||||
summary: format!("{total} open assigned: {breakdown}"),
|
||||
source: None,
|
||||
}
|
||||
};
|
||||
|
||||
match crate::client::request::<_, hive_agent_sock::Response>(socket, &req).await {
|
||||
Ok(_) => debug!(total, "forge_notify: assigned rollup todo updated"),
|
||||
Err(e) => debug!("forge_notify: assigned rollup todo update failed: {e}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
Loading…
Reference in a new issue