Skip to content

Commit d97c5ea

Browse files
committed
feat(notify-zulip): support notify-zulip.<label> getting mapped to multiple actions
1 parent 742b66b commit d97c5ea

File tree

2 files changed

+149
-40
lines changed

2 files changed

+149
-40
lines changed

src/config.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,15 @@ pub(crate) struct AutolabelLabelConfig {
221221
#[derive(PartialEq, Eq, Debug, serde::Deserialize)]
222222
pub(crate) struct NotifyZulipConfig {
223223
#[serde(flatten)]
224-
pub(crate) labels: HashMap<String, NotifyZulipLabelConfig>,
224+
pub(crate) labels: HashMap<String, NotifyZulipNameConfig>,
225+
}
226+
227+
#[derive(PartialEq, Eq, Debug, serde::Deserialize)]
228+
pub(crate) struct NotifyZulipNameConfig {
229+
#[serde(flatten)]
230+
pub(crate) default: Option<NotifyZulipLabelConfig>,
231+
#[serde(flatten)]
232+
pub(crate) others: Option<HashMap<String, NotifyZulipLabelConfig>>,
225233
}
226234

227235
#[derive(PartialEq, Eq, Debug, serde::Deserialize)]

src/handlers/notify_zulip.rs

Lines changed: 140 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
config::{NotifyZulipConfig, NotifyZulipLabelConfig},
2+
config::{NotifyZulipConfig, NotifyZulipLabelConfig, NotifyZulipNameConfig},
33
github::{Issue, IssuesAction, IssuesEvent, Label},
44
handlers::Context,
55
};
@@ -12,6 +12,8 @@ pub(super) struct NotifyZulipInput {
1212
/// For example, if an `I-prioritize` issue is closed,
1313
/// this field will be `I-prioritize`.
1414
label: Label,
15+
is_default_valid: bool,
16+
names: Vec<String>,
1517
}
1618

1719
pub(super) enum NotificationType {
@@ -52,24 +54,67 @@ pub(super) async fn parse_input(
5254
fn parse_label_change_input(
5355
event: &IssuesEvent,
5456
label: Label,
55-
config: &NotifyZulipLabelConfig,
57+
config: &NotifyZulipNameConfig,
5658
) -> Option<NotifyZulipInput> {
57-
if !has_all_required_labels(&event.issue, config) {
58-
// Issue misses a required label, ignore this event
59+
let mut is_default_valid = false;
60+
let mut names: Vec<String> = vec![];
61+
62+
match &config.default {
63+
Some(label_config) => {
64+
if has_all_required_labels(&event.issue, &label_config) {
65+
match event.action {
66+
IssuesAction::Labeled { .. } if !label_config.messages_on_add.is_empty() => {
67+
is_default_valid = true;
68+
}
69+
IssuesAction::Unlabeled { .. } if !label_config.messages_on_remove.is_empty() => {
70+
is_default_valid = true;
71+
}
72+
_ => (),
73+
}
74+
}
75+
}
76+
None => ()
77+
}
78+
79+
match &config.others {
80+
Some(other_configs) => {
81+
for (name, label_config) in other_configs {
82+
if has_all_required_labels(&event.issue, &label_config) {
83+
match event.action {
84+
IssuesAction::Labeled { .. } if !label_config.messages_on_add.is_empty() => {
85+
names.push(name.to_string());
86+
}
87+
IssuesAction::Unlabeled { .. } if !label_config.messages_on_remove.is_empty() => {
88+
names.push(name.to_string());
89+
}
90+
_ => (),
91+
}
92+
}
93+
}
94+
}
95+
None => ()
96+
}
97+
98+
if !is_default_valid && names.is_empty() {
99+
// It seems that there is no match between this event and any notify-zulip config, ignore this event
59100
return None;
60101
}
61102

62103
match event.action {
63-
IssuesAction::Labeled { .. } if !config.messages_on_add.is_empty() => {
104+
IssuesAction::Labeled { .. } => {
64105
Some(NotifyZulipInput {
65106
notification_type: NotificationType::Labeled,
66107
label,
108+
is_default_valid,
109+
names,
67110
})
68111
}
69-
IssuesAction::Unlabeled { .. } if !config.messages_on_remove.is_empty() => {
112+
IssuesAction::Unlabeled { .. } => {
70113
Some(NotifyZulipInput {
71114
notification_type: NotificationType::Unlabeled,
72115
label,
116+
is_default_valid,
117+
names,
73118
})
74119
}
75120
_ => None,
@@ -92,22 +137,65 @@ fn parse_close_reopen_input(
92137
.map(|config| (label, config))
93138
})
94139
.flat_map(|(label, config)| {
95-
if !has_all_required_labels(&event.issue, config) {
96-
// Issue misses a required label, ignore this event
140+
let mut is_default_valid = false;
141+
let mut names: Vec<String> = vec![];
142+
143+
match &config.default {
144+
Some(label_config) => {
145+
if has_all_required_labels(&event.issue, &label_config) {
146+
match event.action {
147+
IssuesAction::Closed if !label_config.messages_on_close.is_empty() => {
148+
is_default_valid = true;
149+
}
150+
IssuesAction::Reopened if !label_config.messages_on_reopen.is_empty() => {
151+
is_default_valid = true;
152+
}
153+
_ => (),
154+
}
155+
}
156+
}
157+
None => ()
158+
}
159+
160+
match &config.others {
161+
Some(other_configs) => {
162+
for (name, label_config) in other_configs {
163+
if has_all_required_labels(&event.issue, &label_config) {
164+
match event.action {
165+
IssuesAction::Closed if !label_config.messages_on_close.is_empty() => {
166+
names.push(name.to_string());
167+
}
168+
IssuesAction::Reopened if !label_config.messages_on_reopen.is_empty() => {
169+
names.push(name.to_string());
170+
}
171+
_ => (),
172+
}
173+
}
174+
}
175+
}
176+
None => ()
177+
}
178+
179+
if !is_default_valid && names.is_empty() {
180+
// It seems that there is no match between this event and any notify-zulip config, ignore this event
97181
return None;
98182
}
99183

100184
match event.action {
101-
IssuesAction::Closed if !config.messages_on_close.is_empty() => {
185+
IssuesAction::Closed => {
102186
Some(NotifyZulipInput {
103187
notification_type: NotificationType::Closed,
104188
label,
189+
is_default_valid,
190+
names,
105191
})
106192
}
107-
IssuesAction::Reopened if !config.messages_on_reopen.is_empty() => {
193+
IssuesAction::Reopened => {
108194
Some(NotifyZulipInput {
109195
notification_type: NotificationType::Reopened,
110196
label,
197+
is_default_valid,
198+
names,
111199
})
112200
}
113201
_ => None,
@@ -140,41 +228,54 @@ pub(super) async fn handle_input<'a>(
140228
inputs: Vec<NotifyZulipInput>,
141229
) -> anyhow::Result<()> {
142230
for input in inputs {
143-
let config = &config.labels[&input.label.name];
144-
145-
let topic = &config.topic;
146-
let topic = topic.replace("{number}", &event.issue.number.to_string());
147-
let mut topic = topic.replace("{title}", &event.issue.title);
148-
// Truncate to 60 chars (a Zulip limitation)
149-
let mut chars = topic.char_indices().skip(59);
150-
if let (Some((len, _)), Some(_)) = (chars.next(), chars.next()) {
151-
topic.truncate(len);
152-
topic.push('…');
231+
let name_config = &config.labels[&input.label.name];
232+
233+
// Get valid label configs
234+
let mut label_configs: Vec<&NotifyZulipLabelConfig> = vec![];
235+
if input.is_default_valid {
236+
label_configs.push(name_config.default.as_ref().unwrap());
237+
}
238+
for name in input.names {
239+
label_configs.push(&name_config.others.as_ref().unwrap()[&name]);
153240
}
154241

155-
let msgs = match input.notification_type {
156-
NotificationType::Labeled => &config.messages_on_add,
157-
NotificationType::Unlabeled => &config.messages_on_remove,
158-
NotificationType::Closed => &config.messages_on_close,
159-
NotificationType::Reopened => &config.messages_on_reopen,
160-
};
242+
for label_config in label_configs {
243+
let config = label_config;
161244

162-
let recipient = crate::zulip::Recipient::Stream {
163-
id: config.zulip_stream,
164-
topic: &topic,
165-
};
245+
let topic = &config.topic;
246+
let topic = topic.replace("{number}", &event.issue.number.to_string());
247+
let mut topic = topic.replace("{title}", &event.issue.title);
248+
// Truncate to 60 chars (a Zulip limitation)
249+
let mut chars = topic.char_indices().skip(59);
250+
if let (Some((len, _)), Some(_)) = (chars.next(), chars.next()) {
251+
topic.truncate(len);
252+
topic.push('…');
253+
}
166254

167-
for msg in msgs {
168-
let msg = msg.replace("{number}", &event.issue.number.to_string());
169-
let msg = msg.replace("{title}", &event.issue.title);
170-
let msg = replace_team_to_be_nominated(&event.issue.labels, msg);
255+
let msgs = match input.notification_type {
256+
NotificationType::Labeled => &config.messages_on_add,
257+
NotificationType::Unlabeled => &config.messages_on_remove,
258+
NotificationType::Closed => &config.messages_on_close,
259+
NotificationType::Reopened => &config.messages_on_reopen,
260+
};
171261

172-
crate::zulip::MessageApiRequest {
173-
recipient,
174-
content: &msg,
262+
let recipient = crate::zulip::Recipient::Stream {
263+
id: config.zulip_stream,
264+
topic: &topic,
265+
};
266+
267+
for msg in msgs {
268+
let msg = msg.replace("{number}", &event.issue.number.to_string());
269+
let msg = msg.replace("{title}", &event.issue.title);
270+
let msg = replace_team_to_be_nominated(&event.issue.labels, msg);
271+
272+
crate::zulip::MessageApiRequest {
273+
recipient,
274+
content: &msg,
275+
}
276+
.send(&ctx.github.raw())
277+
.await?;
175278
}
176-
.send(&ctx.github.raw())
177-
.await?;
178279
}
179280
}
180281

0 commit comments

Comments
 (0)