Skip to content

Commit 1fb8344

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

File tree

2 files changed

+173
-60
lines changed

2 files changed

+173
-60
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: 164 additions & 59 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,26 +54,71 @@ 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 { .. }
70+
if !label_config.messages_on_remove.is_empty() =>
71+
{
72+
is_default_valid = true;
73+
}
74+
_ => (),
75+
}
76+
}
77+
}
78+
None => (),
79+
}
80+
81+
match &config.others {
82+
Some(other_configs) => {
83+
for (name, label_config) in other_configs {
84+
if has_all_required_labels(&event.issue, &label_config) {
85+
match event.action {
86+
IssuesAction::Labeled { .. }
87+
if !label_config.messages_on_add.is_empty() =>
88+
{
89+
names.push(name.to_string());
90+
}
91+
IssuesAction::Unlabeled { .. }
92+
if !label_config.messages_on_remove.is_empty() =>
93+
{
94+
names.push(name.to_string());
95+
}
96+
_ => (),
97+
}
98+
}
99+
}
100+
}
101+
None => (),
102+
}
103+
104+
if !is_default_valid && names.is_empty() {
105+
// It seems that there is no match between this event and any notify-zulip config, ignore this event
59106
return None;
60107
}
61108

62109
match event.action {
63-
IssuesAction::Labeled { .. } if !config.messages_on_add.is_empty() => {
64-
Some(NotifyZulipInput {
65-
notification_type: NotificationType::Labeled,
66-
label,
67-
})
68-
}
69-
IssuesAction::Unlabeled { .. } if !config.messages_on_remove.is_empty() => {
70-
Some(NotifyZulipInput {
71-
notification_type: NotificationType::Unlabeled,
72-
label,
73-
})
74-
}
110+
IssuesAction::Labeled { .. } => Some(NotifyZulipInput {
111+
notification_type: NotificationType::Labeled,
112+
label,
113+
is_default_valid,
114+
names,
115+
}),
116+
IssuesAction::Unlabeled { .. } => Some(NotifyZulipInput {
117+
notification_type: NotificationType::Unlabeled,
118+
label,
119+
is_default_valid,
120+
names,
121+
}),
75122
_ => None,
76123
}
77124
}
@@ -92,24 +139,69 @@ fn parse_close_reopen_input(
92139
.map(|config| (label, config))
93140
})
94141
.flat_map(|(label, config)| {
95-
if !has_all_required_labels(&event.issue, config) {
96-
// Issue misses a required label, ignore this event
142+
let mut is_default_valid = false;
143+
let mut names: Vec<String> = vec![];
144+
145+
match &config.default {
146+
Some(label_config) => {
147+
if has_all_required_labels(&event.issue, &label_config) {
148+
match event.action {
149+
IssuesAction::Closed if !label_config.messages_on_close.is_empty() => {
150+
is_default_valid = true;
151+
}
152+
IssuesAction::Reopened
153+
if !label_config.messages_on_reopen.is_empty() =>
154+
{
155+
is_default_valid = true;
156+
}
157+
_ => (),
158+
}
159+
}
160+
}
161+
None => (),
162+
}
163+
164+
match &config.others {
165+
Some(other_configs) => {
166+
for (name, label_config) in other_configs {
167+
if has_all_required_labels(&event.issue, &label_config) {
168+
match event.action {
169+
IssuesAction::Closed
170+
if !label_config.messages_on_close.is_empty() =>
171+
{
172+
names.push(name.to_string());
173+
}
174+
IssuesAction::Reopened
175+
if !label_config.messages_on_reopen.is_empty() =>
176+
{
177+
names.push(name.to_string());
178+
}
179+
_ => (),
180+
}
181+
}
182+
}
183+
}
184+
None => (),
185+
}
186+
187+
if !is_default_valid && names.is_empty() {
188+
// It seems that there is no match between this event and any notify-zulip config, ignore this event
97189
return None;
98190
}
99191

100192
match event.action {
101-
IssuesAction::Closed if !config.messages_on_close.is_empty() => {
102-
Some(NotifyZulipInput {
103-
notification_type: NotificationType::Closed,
104-
label,
105-
})
106-
}
107-
IssuesAction::Reopened if !config.messages_on_reopen.is_empty() => {
108-
Some(NotifyZulipInput {
109-
notification_type: NotificationType::Reopened,
110-
label,
111-
})
112-
}
193+
IssuesAction::Closed => Some(NotifyZulipInput {
194+
notification_type: NotificationType::Closed,
195+
label,
196+
is_default_valid,
197+
names,
198+
}),
199+
IssuesAction::Reopened => Some(NotifyZulipInput {
200+
notification_type: NotificationType::Reopened,
201+
label,
202+
is_default_valid,
203+
names,
204+
}),
113205
_ => None,
114206
}
115207
})
@@ -140,41 +232,54 @@ pub(super) async fn handle_input<'a>(
140232
inputs: Vec<NotifyZulipInput>,
141233
) -> anyhow::Result<()> {
142234
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('…');
235+
let name_config = &config.labels[&input.label.name];
236+
237+
// Get valid label configs
238+
let mut label_configs: Vec<&NotifyZulipLabelConfig> = vec![];
239+
if input.is_default_valid {
240+
label_configs.push(name_config.default.as_ref().unwrap());
241+
}
242+
for name in input.names {
243+
label_configs.push(&name_config.others.as_ref().unwrap()[&name]);
153244
}
154245

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-
};
246+
for label_config in label_configs {
247+
let config = label_config;
161248

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

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);
259+
let msgs = match input.notification_type {
260+
NotificationType::Labeled => &config.messages_on_add,
261+
NotificationType::Unlabeled => &config.messages_on_remove,
262+
NotificationType::Closed => &config.messages_on_close,
263+
NotificationType::Reopened => &config.messages_on_reopen,
264+
};
171265

172-
crate::zulip::MessageApiRequest {
173-
recipient,
174-
content: &msg,
266+
let recipient = crate::zulip::Recipient::Stream {
267+
id: config.zulip_stream,
268+
topic: &topic,
269+
};
270+
271+
for msg in msgs {
272+
let msg = msg.replace("{number}", &event.issue.number.to_string());
273+
let msg = msg.replace("{title}", &event.issue.title);
274+
let msg = replace_team_to_be_nominated(&event.issue.labels, msg);
275+
276+
crate::zulip::MessageApiRequest {
277+
recipient,
278+
content: &msg,
279+
}
280+
.send(&ctx.github.raw())
281+
.await?;
175282
}
176-
.send(&ctx.github.raw())
177-
.await?;
178283
}
179284
}
180285

0 commit comments

Comments
 (0)