Skip to content

Commit 07b5833

Browse files
Add agenda endpoints to triagebot
This lets users easily fetch up-to-date agendas from triagebot Currently this endpoint is pretty seriously rate-limited, but we can switch that to serving a stale copy instead (refreshing in the background) if this is a problem in practice. I suspect the usage will be rare enough that it won't be a problem.
1 parent 1ffaa97 commit 07b5833

File tree

9 files changed

+263
-129
lines changed

9 files changed

+263
-129
lines changed

Cargo.lock

Lines changed: 43 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ comrak = "0.8.2"
3838
route-recognizer = "0.3.0"
3939
cynic = { version = "0.14" }
4040
itertools = "0.10.2"
41+
tower = { version = "0.4.13", features = ["util", "limit", "buffer", "load-shed"] }
4142

4243
[dependencies.serde]
4344
version = "1"

src/actions.rs

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use chrono::{DateTime, Utc};
22
use std::collections::HashMap;
3+
use std::sync::Arc;
34

45
use async_trait::async_trait;
56
use reqwest::Client;
@@ -10,7 +11,7 @@ use crate::github::{self, GithubClient, Repository};
1011

1112
#[async_trait]
1213
pub trait Action {
13-
async fn call(&self) -> String;
14+
async fn call(&self) -> anyhow::Result<String>;
1415
}
1516

1617
pub struct Step<'a> {
@@ -24,6 +25,7 @@ pub struct Query<'a> {
2425
pub queries: Vec<QueryMap<'a>>,
2526
}
2627

28+
#[derive(Copy, Clone)]
2729
pub enum QueryKind {
2830
List,
2931
Count,
@@ -32,7 +34,7 @@ pub enum QueryKind {
3234
pub struct QueryMap<'a> {
3335
pub name: &'a str,
3436
pub kind: QueryKind,
35-
pub query: Box<dyn github::IssuesQuery + Send + Sync>,
37+
pub query: Arc<dyn github::IssuesQuery + Send + Sync>,
3638
}
3739

3840
#[derive(Debug, serde::Serialize)]
@@ -81,58 +83,65 @@ pub fn to_human(d: DateTime<Utc>) -> String {
8183

8284
#[async_trait]
8385
impl<'a> Action for Step<'a> {
84-
async fn call(&self) -> String {
86+
async fn call(&self) -> anyhow::Result<String> {
8587
let gh = GithubClient::new_with_default_token(Client::new());
8688

8789
let mut context = Context::new();
8890
let mut results = HashMap::new();
8991

92+
let mut handles: Vec<tokio::task::JoinHandle<anyhow::Result<(String, QueryKind, Vec<_>)>>> =
93+
Vec::new();
94+
let semaphore = std::sync::Arc::new(tokio::sync::Semaphore::new(5));
95+
9096
for Query { repos, queries } in &self.actions {
9197
for repo in repos {
9298
let repository = Repository {
9399
full_name: format!("{}/{}", repo.0, repo.1),
94100
};
95101

96102
for QueryMap { name, kind, query } in queries {
97-
let issues = query.query(&repository, name == &"proposed_fcp", &gh).await;
98-
99-
match issues {
100-
Ok(issues_decorator) => match kind {
101-
QueryKind::List => {
102-
results
103-
.entry(*name)
104-
.or_insert(Vec::new())
105-
.extend(issues_decorator);
106-
}
107-
QueryKind::Count => {
108-
let count = issues_decorator.len();
109-
let result = if let Some(value) = context.get(*name) {
110-
value.as_u64().unwrap() + count as u64
111-
} else {
112-
count as u64
113-
};
114-
115-
context.insert(*name, &result);
116-
}
117-
},
118-
Err(err) => {
119-
eprintln!("ERROR: {}", err);
120-
err.chain()
121-
.skip(1)
122-
.for_each(|cause| eprintln!("because: {}", cause));
123-
std::process::exit(1);
124-
}
125-
}
103+
let semaphore = semaphore.clone();
104+
let name = String::from(*name);
105+
let kind = *kind;
106+
let repository = repository.clone();
107+
let gh = gh.clone();
108+
let query = query.clone();
109+
handles.push(tokio::task::spawn(async move {
110+
let _permit = semaphore.acquire().await?;
111+
let issues = query
112+
.query(&repository, name == "proposed_fcp", &gh)
113+
.await?;
114+
Ok((name, kind, issues))
115+
}));
116+
}
117+
}
118+
}
119+
120+
for handle in handles {
121+
let (name, kind, issues) = handle.await.unwrap()?;
122+
match kind {
123+
QueryKind::List => {
124+
results.entry(name).or_insert(Vec::new()).extend(issues);
125+
}
126+
QueryKind::Count => {
127+
let count = issues.len();
128+
let result = if let Some(value) = context.get(&name) {
129+
value.as_u64().unwrap() + count as u64
130+
} else {
131+
count as u64
132+
};
133+
134+
context.insert(name, &result);
126135
}
127136
}
128137
}
129138

130139
for (name, issues) in &results {
131-
context.insert(*name, issues);
140+
context.insert(name, issues);
132141
}
133142

134-
TEMPLATES
143+
Ok(TEMPLATES
135144
.render(&format!("{}.tt", self.name), &context)
136-
.unwrap()
145+
.unwrap())
137146
}
138147
}

0 commit comments

Comments
 (0)