From 821dcdf277c40f9218a5d8c6ce109bd450b69d13 Mon Sep 17 00:00:00 2001 From: "Brian J. Tarricone" Date: Sat, 16 Sep 2023 20:26:59 -0700 Subject: [PATCH] Add support for issue events --- scripts/set-webook.py | 3 +- src/event.rs | 132 +++++++++++++++++++++++++++------ src/main.rs | 21 ++---- test-data/issue-event.json | 145 +++++++++++++++++++++++++++++++++++++ 4 files changed, 261 insertions(+), 40 deletions(-) create mode 100644 test-data/issue-event.json diff --git a/scripts/set-webook.py b/scripts/set-webook.py index 04cff51..4563437 100755 --- a/scripts/set-webook.py +++ b/scripts/set-webook.py @@ -31,6 +31,7 @@ headers = { 'authorization': 'Bearer {}'.format(gitlab_token), } payload = { + "issues_events": True, "merge_requests_events": True, "pipeline_events": True, "push_events": True, @@ -46,7 +47,7 @@ for hook in existing: if hook['url'] == hook_url: upd_url = '{}/{}'.format(gl_base, hook['id']) resp = requests.put(upd_url, headers=headers, json=payload) - print("Updated existing hook") + print("Updated existing hook for {}".format(repo_name)) sys.exit(0) token = secrets.token_urlsafe(32) diff --git a/src/event.rs b/src/event.rs index 74c6db1..ba0f429 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,6 +1,6 @@ pub trait GitlabEventExt { fn project(&self) -> &Project; - fn r#ref(&self) -> &str; + fn r#ref(&self) -> Option<&str>; fn user(&self) -> &str; fn titles(&self) -> Vec; } @@ -109,6 +109,40 @@ pub struct PipelineMergeRequest { pub title: String, } +#[derive(PartialEq, Debug, Deserialize)] +pub enum IssueAction { + #[serde(rename = "open")] + Opened, + #[serde(rename = "close")] + Closed, + #[serde(rename = "reopen")] + Reopened, + #[serde(rename = "update")] + Updated, + #[serde(other)] + Other, +} + +impl IssueAction { + pub fn as_str(&self) -> &str { + match self { + IssueAction::Opened => "opened", + IssueAction::Closed => "closed", + IssueAction::Reopened => "reopened", + IssueAction::Updated => "updated", + IssueAction::Other => "other", + } + } +} + +#[derive(Debug, Deserialize)] +pub struct IssueObjectAttributes { + id: u32, + title: String, + action: IssueAction, + url: String, +} + #[derive(Debug, Deserialize)] #[serde(tag = "object_kind")] pub enum GitlabEvent { @@ -148,6 +182,12 @@ pub enum GitlabEvent { commits: Vec, total_commits_count: u64, }, + #[serde(rename = "issue")] + Issue { + user: User, + project: Project, + object_attributes: IssueObjectAttributes, + }, #[serde(rename = "merge_request")] MergeRequest { user: User, @@ -169,17 +209,19 @@ impl GitlabEventExt for GitlabEvent { match self { GitlabEvent::Push { project, .. } => &project, GitlabEvent::TagPush { project, .. } => &project, + GitlabEvent::Issue { project, .. } => project, GitlabEvent::MergeRequest { project, .. } => &project, GitlabEvent::Pipeline { project, .. } => &project, } } - fn r#ref(&self) -> &str { + fn r#ref(&self) -> Option<&str> { match self { - GitlabEvent::Push { r#ref, .. } => &r#ref, - GitlabEvent::TagPush { r#ref, .. } => &r#ref, - GitlabEvent::MergeRequest { object_attributes, .. } => &object_attributes.target_branch, - GitlabEvent::Pipeline { object_attributes, .. } => &object_attributes.r#ref, + GitlabEvent::Push { r#ref, .. } => Some(&r#ref), + GitlabEvent::TagPush { r#ref, .. } => Some(&r#ref), + GitlabEvent::Issue { .. } => None, + GitlabEvent::MergeRequest { object_attributes, .. } => Some(&object_attributes.target_branch), + GitlabEvent::Pipeline { object_attributes, .. } => Some(&object_attributes.r#ref), } } @@ -187,6 +229,7 @@ impl GitlabEventExt for GitlabEvent { match self { GitlabEvent::Push { user_name, .. } => &user_name, GitlabEvent::TagPush { user_name, .. } => &user_name, + GitlabEvent::Issue { user, .. } => &user.name, GitlabEvent::MergeRequest { user, .. } => &user.name, GitlabEvent::Pipeline { user, .. } => &user.name, } @@ -239,31 +282,52 @@ impl GitlabEventExt for GitlabEvent { let url = format!("{}/-/tags/{}", project.web_url, parse_ref(r#ref)); vec![markdown_link(&title, &url)] } + GitlabEvent::Issue { object_attributes, .. } => { + if object_attributes.action != IssueAction::Other { + let title = format!( + "Issue #{} **{}**: {}", + object_attributes.id, + object_attributes.action.as_str(), + object_attributes.title + ); + vec![markdown_link(&title, &object_attributes.url)] + } else { + vec![] + } + } GitlabEvent::MergeRequest { object_attributes, .. } => { - let title = format!( - "MR !{} **{}**: {}", - object_attributes.iid, - object_attributes.action.as_str(), - object_attributes.title - ); - vec![markdown_link(&title, &object_attributes.url)] + if object_attributes.action != MergeRequestAction::Other { + let title = format!( + "MR !{} **{}**: {}", + object_attributes.iid, + object_attributes.action.as_str(), + object_attributes.title + ); + vec![markdown_link(&title, &object_attributes.url)] + } else { + vec![] + } } GitlabEvent::Pipeline { object_attributes, merge_request, .. } => { - let title = object_attributes - .name - .as_ref() - .map(|n| n.clone()) - .or(merge_request.as_ref().map(|mr| mr.title.clone())) - .iter() - .fold( - format!("Pipeline **{}**", object_attributes.status.as_str()), - |accum, title| format!("{}: {}", accum, title), - ); - vec![markdown_link(&title, &object_attributes.url)] + if object_attributes.status != PipelineStatus::Other { + let title = object_attributes + .name + .as_ref() + .map(|n| n.clone()) + .or(merge_request.as_ref().map(|mr| mr.title.clone())) + .iter() + .fold( + format!("Pipeline **{}**", object_attributes.status.as_str()), + |accum, title| format!("{}: {}", accum, title), + ); + vec![markdown_link(&title, &object_attributes.url)] + } else { + vec![] + } } } } @@ -368,6 +432,26 @@ mod test { Ok(()) } + #[test] + pub fn parse_issue_event() -> anyhow::Result<()> { + let event = load_test_data("issue-event")?; + + match event { + GitlabEvent::Issue { + user, + object_attributes, + .. + } => { + assert_eq!(user.name, "Administrator"); + assert_eq!(object_attributes.id, 301); + assert_eq!(object_attributes.action, IssueAction::Opened); + } + _ => panic!("not an issue event"), + } + + Ok(()) + } + #[test] pub fn parse_merge_request_event() -> anyhow::Result<()> { let event = load_test_data("merge-request-event")?; diff --git a/src/main.rs b/src/main.rs index 2a6fa96..67151a2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,8 +23,6 @@ use matrix_sdk::{ use tokio::sync::mpsc; use warp::Filter; -use crate::event::{MergeRequestAction, PipelineStatus}; - async fn build_sync_settings(matrix_client: &Client) -> SyncSettings { let mut settings = SyncSettings::default().timeout(Duration::from_secs(30)); if let Some(token) = matrix_client.sync_token().await { @@ -107,15 +105,18 @@ async fn ensure_matrix_room_joined(matrix_client: &Client, room_id: &OwnedRoomOr fn build_gitlab_messages(event: &GitlabEvent) -> Vec { let project = event.project(); - let refname = event::parse_ref(event.r#ref()); + let refname = event.r#ref().map(event::parse_ref); event .titles() .iter() .map(|title| { format!( - "\\[{}\\] `{}` *{}* {}", + "\\[{}\\] {}*{}* {}", project.path_with_namespace, - refname, + refname + .as_ref() + .map(|rn| format!("`{}` ", rn)) + .unwrap_or_else(|| "".to_string()), event.user(), title, ) @@ -128,16 +129,6 @@ async fn handle_gitlab_event( room_id: &OwnedRoomOrAliasId, matrix_client: &Client, ) -> anyhow::Result<()> { - if let GitlabEvent::MergeRequest { object_attributes, .. } = &event { - if object_attributes.action == MergeRequestAction::Other { - return Ok(()); - } - } else if let GitlabEvent::Pipeline { object_attributes, .. } = &event { - if object_attributes.status == PipelineStatus::Other { - return Ok(()); - } - } - let room = ensure_matrix_room_joined(matrix_client, room_id).await?; for msg in build_gitlab_messages(&event) { debug!("Sending message to {}: {}", room_id, msg); diff --git a/test-data/issue-event.json b/test-data/issue-event.json new file mode 100644 index 0000000..bfa97cb --- /dev/null +++ b/test-data/issue-event.json @@ -0,0 +1,145 @@ +{ + "object_kind": "issue", + "event_type": "issue", + "user": { + "id": 1, + "name": "Administrator", + "username": "root", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon", + "email": "admin@example.com" + }, + "project": { + "id": 1, + "name":"Gitlab Test", + "description":"Aut reprehenderit ut est.", + "web_url":"http://example.com/gitlabhq/gitlab-test", + "avatar_url":null, + "git_ssh_url":"git@example.com:gitlabhq/gitlab-test.git", + "git_http_url":"http://example.com/gitlabhq/gitlab-test.git", + "namespace":"GitlabHQ", + "visibility_level":20, + "path_with_namespace":"gitlabhq/gitlab-test", + "default_branch":"master", + "ci_config_path": null, + "homepage":"http://example.com/gitlabhq/gitlab-test", + "url":"http://example.com/gitlabhq/gitlab-test.git", + "ssh_url":"git@example.com:gitlabhq/gitlab-test.git", + "http_url":"http://example.com/gitlabhq/gitlab-test.git" + }, + "object_attributes": { + "id": 301, + "title": "New API: create/update/delete file", + "assignee_ids": [51], + "assignee_id": 51, + "author_id": 51, + "project_id": 14, + "created_at": "2013-12-03T17:15:43Z", + "updated_at": "2013-12-03T17:15:43Z", + "updated_by_id": 1, + "last_edited_at": null, + "last_edited_by_id": null, + "relative_position": 0, + "description": "Create new API for manipulations with repository", + "milestone_id": null, + "state_id": 1, + "confidential": false, + "discussion_locked": true, + "due_date": null, + "moved_to_id": null, + "duplicated_to_id": null, + "time_estimate": 0, + "total_time_spent": 0, + "time_change": 0, + "human_total_time_spent": null, + "human_time_estimate": null, + "human_time_change": null, + "weight": null, + "health_status": "at_risk", + "iid": 23, + "url": "http://example.com/diaspora/issues/23", + "state": "opened", + "action": "open", + "severity": "high", + "escalation_status": "triggered", + "escalation_policy": { + "id": 18, + "name": "Engineering On-call" + }, + "labels": [{ + "id": 206, + "title": "API", + "color": "#ffffff", + "project_id": 14, + "created_at": "2013-12-03T17:15:43Z", + "updated_at": "2013-12-03T17:15:43Z", + "template": false, + "description": "API related issues", + "type": "ProjectLabel", + "group_id": 41 + }] + }, + "repository": { + "name": "Gitlab Test", + "url": "http://example.com/gitlabhq/gitlab-test.git", + "description": "Aut reprehenderit ut est.", + "homepage": "http://example.com/gitlabhq/gitlab-test" + }, + "assignees": [{ + "name": "User1", + "username": "user1", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" + }], + "assignee": { + "name": "User1", + "username": "user1", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" + }, + "labels": [{ + "id": 206, + "title": "API", + "color": "#ffffff", + "project_id": 14, + "created_at": "2013-12-03T17:15:43Z", + "updated_at": "2013-12-03T17:15:43Z", + "template": false, + "description": "API related issues", + "type": "ProjectLabel", + "group_id": 41 + }], + "changes": { + "updated_by_id": { + "previous": null, + "current": 1 + }, + "updated_at": { + "previous": "2017-09-15 16:50:55 UTC", + "current": "2017-09-15 16:52:00 UTC" + }, + "labels": { + "previous": [{ + "id": 206, + "title": "API", + "color": "#ffffff", + "project_id": 14, + "created_at": "2013-12-03T17:15:43Z", + "updated_at": "2013-12-03T17:15:43Z", + "template": false, + "description": "API related issues", + "type": "ProjectLabel", + "group_id": 41 + }], + "current": [{ + "id": 205, + "title": "Platform", + "color": "#123123", + "project_id": 14, + "created_at": "2013-12-03T17:15:43Z", + "updated_at": "2013-12-03T17:15:43Z", + "template": false, + "description": "Platform related issues", + "type": "ProjectLabel", + "group_id": 41 + }] + } + } +}