feat(transformers): Add MR transformer and polymorphic discussion support
Introduces NormalizedMergeRequest transformer and updates discussion
normalization to handle both issue and MR discussions polymorphically.
New transformers:
- NormalizedMergeRequest: Transforms API MergeRequest to database row,
extracting labels/assignees/reviewers into separate collections for
junction table insertion. Handles draft detection, detailed_merge_status
preference over deprecated merge_status, and merge_user over merged_by.
Discussion transformer updates:
- NormalizedDiscussion now takes noteable_type ("Issue" | "MergeRequest")
and noteable_id for polymorphic FK binding
- normalize_discussions_for_issue(): Convenience wrapper for issues
- normalize_discussions_for_mr(): Convenience wrapper for MRs
- DiffNote position fields (type, line_range, SHA triplet) now extracted
from API position object for code review context
Design decisions:
- Transformer returns (normalized_item, labels, assignees, reviewers)
tuple for efficient batch insertion without re-querying
- Timestamps converted to ms epoch for SQLite storage consistency
- Optional fields use map() chains for clean null handling
The polymorphic discussion approach allows reusing the same discussions
and notes tables for both issues and MRs, with noteable_type + FK
determining the parent relationship.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,8 +24,8 @@ pub struct IssueRow {
|
||||
pub created_at: i64, // ms epoch UTC
|
||||
pub updated_at: i64, // ms epoch UTC
|
||||
pub web_url: String,
|
||||
pub due_date: Option<String>, // YYYY-MM-DD
|
||||
pub milestone_title: Option<String>, // Denormalized for quick display
|
||||
pub due_date: Option<String>, // YYYY-MM-DD
|
||||
pub milestone_title: Option<String>, // Denormalized for quick display
|
||||
}
|
||||
|
||||
/// Local schema representation of a milestone row.
|
||||
@@ -62,11 +62,8 @@ pub fn transform_issue(issue: GitLabIssue) -> Result<IssueWithMetadata, Transfor
|
||||
let created_at = parse_timestamp(&issue.created_at)?;
|
||||
let updated_at = parse_timestamp(&issue.updated_at)?;
|
||||
|
||||
let assignee_usernames: Vec<String> = issue
|
||||
.assignees
|
||||
.iter()
|
||||
.map(|a| a.username.clone())
|
||||
.collect();
|
||||
let assignee_usernames: Vec<String> =
|
||||
issue.assignees.iter().map(|a| a.username.clone()).collect();
|
||||
|
||||
let milestone_title = issue.milestone.as_ref().map(|m| m.title.clone());
|
||||
|
||||
@@ -252,7 +249,10 @@ mod tests {
|
||||
assert_eq!(milestone.description, Some("First release".to_string()));
|
||||
assert_eq!(milestone.state, Some("active".to_string()));
|
||||
assert_eq!(milestone.due_date, Some("2024-02-01".to_string()));
|
||||
assert_eq!(milestone.web_url, Some("https://gitlab.example.com/-/milestones/5".to_string()));
|
||||
assert_eq!(
|
||||
milestone.web_url,
|
||||
Some("https://gitlab.example.com/-/milestones/5".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user