Skip to content

Commit a801854

Browse files
authored
Merge pull request #2032 from Urgau/editissuebody-to-db
Migrate `EditIssueBody` to database state storage
2 parents 71f8d2e + d85d224 commit a801854

File tree

4 files changed

+127
-96
lines changed

4 files changed

+127
-96
lines changed

src/handlers/assign.rs

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ struct Reviewers {
112112
}
113113

114114
/// Assignment data stored in the issue/PR body.
115-
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
115+
#[derive(Debug, Clone, PartialEq, Default, serde::Serialize, serde::Deserialize)]
116116
struct AssignData {
117117
user: Option<String>,
118118
}
@@ -616,7 +616,10 @@ pub(super) async fn handle_command(
616616

617617
set_assignee(ctx, issue, &ctx.github, &assignee).await?;
618618
} else {
619-
let e = EditIssueBody::new(&issue, "ASSIGN");
619+
let mut client = ctx.db.get().await;
620+
let mut e: EditIssueBody<'_, AssignData> =
621+
EditIssueBody::load(&mut client, &issue, "ASSIGN").await?;
622+
let d = e.data_mut();
620623

621624
let to_assign = match cmd {
622625
AssignCommand::Claim => event.user().login.clone(),
@@ -627,14 +630,14 @@ pub(super) async fn handle_command(
627630
username.clone()
628631
}
629632
AssignCommand::ReleaseAssignment => {
630-
if let Some(AssignData {
633+
if let AssignData {
631634
user: Some(current),
632-
}) = e.current_data()
635+
} = d
633636
{
634-
if current == event.user().login || is_team_member {
637+
if *current == event.user().login || is_team_member {
635638
issue.remove_assignees(&ctx.github, Selection::All).await?;
636-
e.apply(&ctx.github, String::new(), AssignData { user: None })
637-
.await?;
639+
*d = AssignData { user: None };
640+
e.apply(&ctx.github, String::new()).await?;
638641
return Ok(());
639642
} else {
640643
bail!("Cannot release another user's assignment");
@@ -645,8 +648,8 @@ pub(super) async fn handle_command(
645648
issue
646649
.remove_assignees(&ctx.github, Selection::One(&current))
647650
.await?;
648-
e.apply(&ctx.github, String::new(), AssignData { user: None })
649-
.await?;
651+
*d = AssignData { user: None };
652+
e.apply(&ctx.github, String::new()).await?;
650653
return Ok(());
651654
} else {
652655
bail!("Cannot release unassigned issue");
@@ -664,14 +667,15 @@ pub(super) async fn handle_command(
664667
);
665668
return Ok(());
666669
}
667-
let data = AssignData {
670+
*d = AssignData {
668671
user: Some(to_assign.clone()),
669672
};
670673

671-
e.apply(&ctx.github, String::new(), &data).await?;
672-
673674
match issue.set_assignee(&ctx.github, &to_assign).await {
674-
Ok(()) => return Ok(()), // we are done
675+
Ok(()) => {
676+
e.apply(&ctx.github, String::new()).await?;
677+
return Ok(());
678+
} // we are done
675679
Err(github::AssignmentError::InvalidAssignee) => {
676680
issue
677681
.set_assignee(&ctx.github, &ctx.username)
@@ -682,7 +686,7 @@ pub(super) async fn handle_command(
682686
to_assign,
683687
event.html_url().unwrap()
684688
);
685-
e.apply(&ctx.github, cmt_body, &data).await?;
689+
e.apply(&ctx.github, cmt_body).await?;
686690
}
687691
Err(e) => return Err(e.into()),
688692
}

src/handlers/concern.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,20 @@ use parser::command::concern::ConcernCommand;
1313

1414
const CONCERN_ISSUE_KEY: &str = "CONCERN-ISSUE";
1515

16-
#[derive(Debug, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
16+
#[derive(Debug, PartialEq, Eq, Default, Clone, serde::Serialize, serde::Deserialize)]
1717
struct ConcernData {
1818
concerns: Vec<Concern>,
1919
}
2020

21-
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
21+
#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize)]
2222
struct Concern {
2323
title: String,
2424
author: String,
2525
comment_url: String,
2626
status: ConcernStatus,
2727
}
2828

29-
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
29+
#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize)]
3030
enum ConcernStatus {
3131
Active,
3232
Resolved { comment_url: String },
@@ -84,8 +84,10 @@ pub(super) async fn handle_command(
8484
return Ok(());
8585
}
8686

87-
let edit = EditIssueBody::new(&issue, CONCERN_ISSUE_KEY);
88-
let mut concern_data: ConcernData = edit.current_data().unwrap_or_default();
87+
let mut client = ctx.db.get().await;
88+
let mut edit: EditIssueBody<'_, ConcernData> =
89+
EditIssueBody::load(&mut client, &issue, CONCERN_ISSUE_KEY).await?;
90+
let concern_data = edit.data_mut();
8991

9092
// Process the command by either adding a new concern or resolving the old one
9193
match cmd {
@@ -151,7 +153,7 @@ pub(super) async fn handle_command(
151153
}
152154

153155
// Apply the new markdown concerns list to the issue
154-
edit.apply(&ctx.github, new_content, concern_data)
156+
edit.apply(&ctx.github, new_content)
155157
.await
156158
.context("failed to apply the new concerns section markdown")?;
157159

src/handlers/note.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ impl PartialOrd for NoteDataEntry {
6969
}
7070
}
7171

72-
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, Default)]
72+
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, Default, Clone)]
7373
struct NoteData {
7474
entries_by_url: HashMap<String, NoteDataEntry>,
7575
}
@@ -122,9 +122,11 @@ pub(super) async fn handle_command(
122122
cmd: NoteCommand,
123123
) -> anyhow::Result<()> {
124124
let issue = event.issue().unwrap();
125-
let e = EditIssueBody::new(&issue, "SUMMARY");
126125

127-
let mut current: NoteData = e.current_data().unwrap_or_default();
126+
let mut client = ctx.db.get().await;
127+
let mut e: EditIssueBody<'_, NoteData> =
128+
EditIssueBody::load(&mut client, &issue, "SUMMARY").await?;
129+
let current = e.data_mut();
128130

129131
let comment_url = String::from(event.html_url().unwrap());
130132
let author = event.user().login.to_owned();
@@ -158,7 +160,7 @@ pub(super) async fn handle_command(
158160
let new_markdown = current.to_markdown(&ctx.username);
159161
log::debug!("New MD: {:#?}", new_markdown);
160162

161-
e.apply(&ctx.github, new_markdown, current).await?;
163+
e.apply(&ctx.github, new_markdown).await?;
162164

163165
Ok(())
164166
}

src/interactions.rs

Lines changed: 95 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
use crate::github::{GithubClient, Issue};
1+
use anyhow::Result;
2+
use serde::{Deserialize, Serialize};
3+
use tokio_postgres::Client as DbClient;
4+
5+
use crate::{
6+
db::issue_data::IssueData,
7+
github::{GithubClient, Issue},
8+
};
29
use std::fmt::Write;
310

411
pub struct ErrorComment<'a> {
@@ -51,7 +58,11 @@ impl<'a> PingComment<'a> {
5158
}
5259
}
5360

54-
pub struct EditIssueBody<'a> {
61+
pub struct EditIssueBody<'a, T>
62+
where
63+
T: for<'t> Deserialize<'t> + Serialize + Default + std::fmt::Debug + Sync + PartialEq + Clone,
64+
{
65+
issue_data: IssueData<'a, T>,
5566
issue: &'a Issue,
5667
id: &'static str,
5768
}
@@ -63,88 +74,47 @@ fn normalize_body(body: &str) -> String {
6374
str::replace(body, "\r\n", "\n")
6475
}
6576

66-
impl<'a> EditIssueBody<'a> {
67-
pub fn new(issue: &'a Issue, id: &'static str) -> EditIssueBody<'a> {
68-
EditIssueBody { issue, id }
69-
}
70-
71-
fn get_current(&self) -> Option<String> {
72-
let self_issue_body = normalize_body(&self.issue.body);
73-
let start_section = self.start_section();
74-
let end_section = self.end_section();
75-
if self_issue_body.contains(START_BOT) {
76-
if self_issue_body.contains(&start_section) {
77-
let start_idx = self_issue_body.find(&start_section).unwrap();
78-
let end_idx = self_issue_body.find(&end_section).unwrap();
79-
let current =
80-
String::from(&self_issue_body[start_idx..(end_idx + end_section.len())]);
81-
Some(current)
82-
} else {
83-
None
84-
}
85-
} else {
86-
None
77+
impl<'a, T> EditIssueBody<'a, T>
78+
where
79+
T: for<'t> Deserialize<'t> + Serialize + Default + std::fmt::Debug + Sync + PartialEq + Clone,
80+
{
81+
pub async fn load(
82+
db: &'a mut DbClient,
83+
issue: &'a Issue,
84+
id: &'static str,
85+
) -> Result<EditIssueBody<'a, T>> {
86+
let issue_data = IssueData::load(db, issue, id).await?;
87+
88+
let mut edit = EditIssueBody {
89+
issue_data,
90+
issue,
91+
id,
92+
};
93+
94+
// Legacy, if we find data inside the issue body for the current
95+
// id, use that instead of the (hopefully) default value given
96+
// by IssueData.
97+
if let Some(d) = edit.current_data_markdown() {
98+
edit.issue_data.data = d;
8799
}
88-
}
89-
90-
pub fn current_data<T: serde::de::DeserializeOwned>(&self) -> Option<T> {
91-
let all = self.get_current()?;
92-
let start = self.data_section_start();
93-
let end = self.data_section_end();
94-
let start_idx = all.find(&start).unwrap();
95-
let end_idx = all.find(&end).unwrap();
96-
let text = &all[(start_idx + start.len())..end_idx];
97-
Some(serde_json::from_str(text).unwrap_or_else(|e| {
98-
panic!("deserializing data {:?} failed: {:?}", text, e);
99-
}))
100-
}
101-
102-
fn start_section(&self) -> String {
103-
format!("<!-- TRIAGEBOT_{}_START -->\n", self.id)
104-
}
105-
106-
fn end_section(&self) -> String {
107-
format!("\n<!-- TRIAGEBOT_{}_END -->\n", self.id)
108-
}
109-
110-
fn data_section_start(&self) -> String {
111-
format!("\n<!-- TRIAGEBOT_{}_DATA_START$$", self.id)
112-
}
113100

114-
fn data_section_end(&self) -> String {
115-
format!("$$TRIAGEBOT_{}_DATA_END -->\n", self.id)
101+
Ok(edit)
116102
}
117103

118-
fn data_section<T>(&self, data: T) -> String
119-
where
120-
T: serde::Serialize,
121-
{
122-
format!(
123-
"{}{}{}",
124-
self.data_section_start(),
125-
serde_json::to_string(&data).unwrap(),
126-
self.data_section_end()
127-
)
104+
pub fn data_mut(&mut self) -> &mut T {
105+
&mut self.issue_data.data
128106
}
129107

130-
pub async fn apply<T>(&self, client: &GithubClient, text: String, data: T) -> anyhow::Result<()>
131-
where
132-
T: serde::Serialize,
133-
{
108+
pub async fn apply(self, client: &GithubClient, text: String) -> anyhow::Result<()> {
134109
let mut current_body = normalize_body(&self.issue.body.clone());
135110
let start_section = self.start_section();
136111
let end_section = self.end_section();
137112

138-
let bot_section = format!(
139-
"{}{}{}{}",
140-
start_section,
141-
text,
142-
self.data_section(data),
143-
end_section
144-
);
113+
let bot_section = format!("{}{}{}", start_section, text, end_section);
145114
let empty_bot_section = format!("{}{}", start_section, end_section);
146-
147115
let all_new = format!("\n\n{}{}{}", START_BOT, bot_section, END_BOT);
116+
117+
// Edit or add the new text the current triagebot section
148118
if current_body.contains(START_BOT) {
149119
if current_body.contains(&start_section) {
150120
let start_idx = current_body.find(&start_section).unwrap();
@@ -166,6 +136,59 @@ impl<'a> EditIssueBody<'a> {
166136

167137
self.issue.edit_body(&client, &new_body).await?;
168138
}
139+
140+
// Save the state in the database
141+
self.issue_data.save().await?;
142+
169143
Ok(())
170144
}
145+
146+
fn start_section(&self) -> String {
147+
format!("<!-- TRIAGEBOT_{}_START -->\n", self.id)
148+
}
149+
150+
fn end_section(&self) -> String {
151+
format!("\n<!-- TRIAGEBOT_{}_END -->\n", self.id)
152+
}
153+
154+
// Legacy, only used for handling data inside the issue body it-self
155+
156+
fn current_data_markdown(&self) -> Option<T> {
157+
let all = self.get_current_markdown()?;
158+
let start = self.data_section_start();
159+
let end = self.data_section_end();
160+
let start_idx = all.find(&start).unwrap();
161+
let end_idx = all.find(&end).unwrap();
162+
let text = &all[(start_idx + start.len())..end_idx];
163+
Some(serde_json::from_str(text).unwrap_or_else(|e| {
164+
panic!("deserializing data {:?} failed: {:?}", text, e);
165+
}))
166+
}
167+
168+
fn get_current_markdown(&self) -> Option<String> {
169+
let self_issue_body = normalize_body(&self.issue.body);
170+
let start_section = self.start_section();
171+
let end_section = self.end_section();
172+
if self_issue_body.contains(START_BOT) {
173+
if self_issue_body.contains(&start_section) {
174+
let start_idx = self_issue_body.find(&start_section).unwrap();
175+
let end_idx = self_issue_body.find(&end_section).unwrap();
176+
let current =
177+
String::from(&self_issue_body[start_idx..(end_idx + end_section.len())]);
178+
Some(current)
179+
} else {
180+
None
181+
}
182+
} else {
183+
None
184+
}
185+
}
186+
187+
fn data_section_start(&self) -> String {
188+
format!("\n<!-- TRIAGEBOT_{}_DATA_START$$", self.id)
189+
}
190+
191+
fn data_section_end(&self) -> String {
192+
format!("$$TRIAGEBOT_{}_DATA_END -->\n", self.id)
193+
}
171194
}

0 commit comments

Comments
 (0)