Skip to content

Commit a9b9d60

Browse files
authored
feat(mono): add mention reference on comment (#1408)
1 parent 536746b commit a9b9d60

14 files changed

Lines changed: 231 additions & 9 deletions

File tree

ceres/src/merge_checker/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,16 @@ pub struct CheckResult {
9494
pub struct CheckerRegistry {
9595
checkers: HashMap<CheckType, Box<dyn Checker>>,
9696
storage: Arc<Storage>,
97+
#[allow(dead_code)]
98+
username: String,
9799
}
98100

99101
impl CheckerRegistry {
100-
pub fn new(storage: Arc<Storage>) -> Self {
102+
pub fn new(storage: Arc<Storage>, username: String) -> Self {
101103
let mut r = CheckerRegistry {
102104
checkers: HashMap::new(),
103105
storage: storage.clone(),
106+
username,
104107
};
105108
r.register(CheckType::MrSync, Box::new(MrSyncChecker { storage }));
106109
r

ceres/src/pack/monorepo.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ impl MonoRepo {
539539
.await?
540540
.expect("MR Not Found");
541541

542-
let check_reg = CheckerRegistry::new(self.storage.clone().into());
542+
let check_reg = CheckerRegistry::new(self.storage.clone().into(), self.username());
543543
check_reg.run_checks(mr_info.into()).await?;
544544
Ok(())
545545
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.15
2+
3+
use super::sea_orm_active_enums::ReferenceTypeEnum;
4+
use sea_orm::entity::prelude::*;
5+
use serde::{Deserialize, Serialize};
6+
7+
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
8+
#[sea_orm(table_name = "issue_mr_references")]
9+
pub struct Model {
10+
pub created_at: DateTime,
11+
pub updated_at: DateTime,
12+
#[sea_orm(primary_key, auto_increment = false)]
13+
pub source_id: String,
14+
#[sea_orm(primary_key, auto_increment = false)]
15+
pub target_id: String,
16+
pub reference_type: ReferenceTypeEnum,
17+
}
18+
19+
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
20+
pub enum Relation {}
21+
22+
impl ActiveModelBehavior for ActiveModel {}

jupiter/callisto/src/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub mod git_tag;
1414
pub mod git_tree;
1515
pub mod gpg_key;
1616
pub mod import_refs;
17+
pub mod issue_mr_references;
1718
pub mod item_assignees;
1819
pub mod item_labels;
1920
pub mod label;

jupiter/callisto/src/prelude.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub use super::git_tag::Entity as GitTag;
1111
pub use super::git_tree::Entity as GitTree;
1212
pub use super::gpg_key::Entity as GpgKey;
1313
pub use super::import_refs::Entity as ImportRefs;
14+
pub use super::issue_mr_references::Entity as IssueMrReferences;
1415
pub use super::item_assignees::Entity as ItemAssignees;
1516
pub use super::item_labels::Entity as ItemLabels;
1617
pub use super::label::Entity as Label;

jupiter/callisto/src/sea_orm_active_enums.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ pub enum ConvTypeEnum {
5050
Label,
5151
#[sea_orm(string_value = "assignee")]
5252
Assignee,
53+
#[sea_orm(string_value = "mention")]
54+
Mention,
5355
}
5456
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
5557
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "merge_status_enum")]
@@ -70,6 +72,20 @@ pub enum RefTypeEnum {
7072
Tag,
7173
}
7274
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
75+
#[sea_orm(
76+
rs_type = "String",
77+
db_type = "Enum",
78+
enum_name = "reference_type_enum"
79+
)]
80+
pub enum ReferenceTypeEnum {
81+
#[sea_orm(string_value = "mention")]
82+
Mention,
83+
#[sea_orm(string_value = "build_relates")]
84+
BuildRelates,
85+
#[sea_orm(string_value = "blocks")]
86+
Blocks,
87+
}
88+
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
7389
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "storage_type_enum")]
7490
pub enum StorageTypeEnum {
7591
#[sea_orm(string_value = "database")]
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use sea_orm::{sea_query::extension::postgres::Type, DatabaseBackend, EnumIter, Iterable};
2+
use sea_orm_migration::{prelude::*, schema::*};
3+
4+
#[derive(DeriveMigrationName)]
5+
pub struct Migration;
6+
7+
#[async_trait::async_trait]
8+
impl MigrationTrait for Migration {
9+
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
10+
let backend = manager.get_database_backend();
11+
12+
match backend {
13+
DatabaseBackend::Postgres => {
14+
manager
15+
.create_type(
16+
Type::create()
17+
.as_enum(ReferenceTypeEnum)
18+
.values(ReferenceType::iter())
19+
.to_owned(),
20+
)
21+
.await?;
22+
23+
manager
24+
.get_connection()
25+
.execute_unprepared(
26+
r#"ALTER TYPE conv_type_enum ADD VALUE IF NOT EXISTS 'mention';"#,
27+
)
28+
.await?;
29+
}
30+
DatabaseBackend::MySql | DatabaseBackend::Sqlite => {}
31+
}
32+
33+
manager
34+
.create_table(
35+
table_auto(IssueMrReferences::Table)
36+
.if_not_exists()
37+
.col(string(IssueMrReferences::SourceId))
38+
.col(string(IssueMrReferences::TargetId))
39+
.col(enumeration(
40+
IssueMrReferences::ReferenceType,
41+
Alias::new("reference_type_enum"),
42+
ReferenceType::iter(),
43+
))
44+
.primary_key(
45+
Index::create()
46+
.col(IssueMrReferences::SourceId)
47+
.col(IssueMrReferences::TargetId),
48+
)
49+
.to_owned(),
50+
)
51+
.await?;
52+
Ok(())
53+
}
54+
55+
async fn down(&self, _: &SchemaManager) -> Result<(), DbErr> {
56+
Ok(())
57+
}
58+
}
59+
60+
#[derive(DeriveIden)]
61+
enum IssueMrReferences {
62+
Table,
63+
SourceId,
64+
TargetId,
65+
ReferenceType,
66+
}
67+
68+
#[derive(DeriveIden)]
69+
struct ReferenceTypeEnum;
70+
71+
#[derive(Iden, EnumIter)]
72+
pub enum ReferenceType {
73+
Mention,
74+
BuildRelates,
75+
Blocks,
76+
}

jupiter/src/migration/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ mod m20250821_083749_add_checks;
5454
mod m20250828_092459_remove_gpg_table;
5555
mod m20250828_092729_create_standalone_table;
5656
mod m20250903_013904_create_task_table;
57+
mod m20250903_071928_add_issue_refs;
5758
/// Creates a primary key column definition with big integer type.
5859
///
5960
/// # Arguments
@@ -95,6 +96,7 @@ impl MigratorTrait for Migrator {
9596
Box::new(m20250828_092459_remove_gpg_table::Migration),
9697
Box::new(m20250828_092729_create_standalone_table::Migration),
9798
Box::new(m20250903_013904_create_task_table::Migration),
99+
Box::new(m20250903_071928_add_issue_refs::Migration),
98100
]
99101
}
100102
}

jupiter/src/storage/issue_storage.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use std::collections::HashMap;
22
use std::ops::Deref;
33

4+
use callisto::sea_orm_active_enums::ReferenceTypeEnum;
45
use sea_orm::prelude::Expr;
56
use sea_orm::{
67
ActiveModelTrait, ColumnTrait, Condition, EntityTrait, IntoActiveModel, JoinType,
78
PaginatorTrait, QueryFilter, QuerySelect, RelationTrait, Set, TransactionTrait,
89
};
910

10-
use callisto::{item_assignees, item_labels, label, mega_conversation, mega_issue};
11+
use callisto::{issue_mr_references, item_assignees, item_labels, label, mega_conversation, mega_issue};
1112
use common::errors::MegaError;
1213
use common::model::Pagination;
1314

@@ -350,6 +351,28 @@ impl IssueStorage {
350351

351352
Ok(())
352353
}
354+
355+
pub async fn add_reference(
356+
&self,
357+
source_id: &str,
358+
target_id: &str,
359+
reference_type: ReferenceTypeEnum,
360+
) -> Result<issue_mr_references::Model, MegaError> {
361+
let issue_ref = issue_mr_references::Model {
362+
source_id: source_id.to_owned(),
363+
target_id: target_id.to_owned(),
364+
reference_type,
365+
created_at: chrono::Utc::now().naive_utc(),
366+
updated_at: chrono::Utc::now().naive_utc(),
367+
};
368+
369+
let res = issue_ref
370+
.into_active_model()
371+
.insert(self.get_connection())
372+
.await?;
373+
374+
Ok(res)
375+
}
353376
}
354377

355378
fn filter_by_author(cond: Condition, author: Option<String>) -> Condition {

mono/src/api/api_common/comment.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use std::collections::HashSet;
2+
3+
use axum::{extract::State, Json};
4+
use regex::Regex;
5+
6+
use callisto::sea_orm_active_enums::{ConvTypeEnum, ReferenceTypeEnum};
7+
use common::model::CommonResult;
8+
9+
use crate::api::error::ApiError;
10+
use crate::api::oauth::model::LoginUser;
11+
use crate::api::MonoApiServiceState;
12+
13+
pub fn parse_data_id(comment: &str) -> HashSet<String> {
14+
let data_id = Regex::new(r#"data-id="([A-Za-z0-9]+)""#).unwrap();
15+
let links: HashSet<String> = data_id
16+
.captures_iter(comment)
17+
.map(|cap| cap[1].to_string())
18+
.collect();
19+
links
20+
}
21+
22+
pub async fn check_comment_ref(
23+
user: LoginUser,
24+
state: State<MonoApiServiceState>,
25+
comment: &str,
26+
source_link: &str,
27+
) -> Result<Json<CommonResult<()>>, ApiError> {
28+
let links = parse_data_id(comment);
29+
let username = user.username;
30+
for ref_link in links {
31+
state
32+
.issue_stg()
33+
.add_reference(source_link, &ref_link, ReferenceTypeEnum::Mention)
34+
.await?;
35+
state
36+
.conv_stg()
37+
.add_conversation(
38+
&ref_link,
39+
&username,
40+
Some(format!("{username} mentioned this on")),
41+
ConvTypeEnum::Mention,
42+
)
43+
.await?;
44+
}
45+
46+
Ok(Json(CommonResult::success(None)))
47+
}
48+
49+
#[cfg(test)]
50+
mod test {
51+
use std::collections::HashSet;
52+
53+
use crate::api::api_common::comment::parse_data_id;
54+
55+
#[test]
56+
pub fn test_parse_data_id_from_comment() {
57+
let single_ref = r#"<p><span class="link-issue" data-type="linkIssue" data-id="HDQL6ATY" data-label="HDQL6ATY" data-suggestiontype="merge_request">$HDQL6ATY</span> </p>"#;
58+
let data_id = parse_data_id(single_ref);
59+
assert_eq!(
60+
data_id.iter().next(),
61+
Some(String::from("HDQL6ATY")).as_ref()
62+
);
63+
64+
let normal_comment = r#"<p>This is a normal comment without reference</p>"#;
65+
let data_id = parse_data_id(normal_comment);
66+
assert_eq!(data_id.iter().next(), None);
67+
68+
let multi_referene = r#"<p>multireference?? <span class="link-issue" data-type="linkIssue" data-id="PIXLXMS9" data-label="PIXLXMS9" data-suggestiontype="issue">$PIXLXMS9</span> <span class="link-issue" data-type="linkIssue" data-id="PIXLXMS9" data-label="PIXLXMS9" data-suggestiontype="issue">$PIXLXMS9</span> <span class="link-issue" data-type="linkIssue" data-id="ZE710J7U" data-label="ZE710J7U" data-suggestiontype="merge_request">$ZE710J7U</span> </p>"#;
69+
let data_id = parse_data_id(multi_referene);
70+
assert_eq!(
71+
data_id,
72+
HashSet::from([String::from("PIXLXMS9"), String::from("ZE710J7U")])
73+
);
74+
}
75+
}

0 commit comments

Comments
 (0)