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;
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue