fix(discussion): Make NormalizedDiscussion polymorphic for MR support

This is a P0 fix from the CP1-CP2 alignment audit. The original
NormalizedDiscussion struct had issue_id as a non-optional i64 and
hardcoded noteable_type to "Issue", making it incompatible with merge
request discussions even though the database schema already supports
both via nullable columns and a CHECK constraint.

Changes:
- Add NoteableRef enum with Issue(i64) and MergeRequest(i64) variants
  to provide compile-time safety against mixing up issue vs MR IDs
- Change NormalizedDiscussion.issue_id from i64 to Option<i64>
- Add NormalizedDiscussion.merge_request_id: Option<i64>
- Update transform_discussion() signature to take NoteableRef instead
  of local_issue_id, deriving issue_id/merge_request_id/noteable_type
  from the enum variant
- Update upsert_discussion() SQL to include merge_request_id column
  (now 12 parameters instead of 11)
- Export NoteableRef from transformers module
- Add test for MergeRequest discussion transformation
- Update all existing tests to use NoteableRef::Issue(id)

The database schema (migration 002) was forward-thinking and already
supports both issue_id and merge_request_id as nullable columns with
a CHECK constraint. This change prepares the application layer for
CP2 merge request support without requiring any migrations.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Taylor Eernisse
2026-01-26 17:00:49 -05:00
parent fbdfd8f4cb
commit d9d749ac57
3 changed files with 63 additions and 22 deletions

View File

@@ -14,7 +14,7 @@ use crate::Config;
use crate::core::error::Result;
use crate::core::payloads::{StorePayloadOptions, store_payload};
use crate::gitlab::GitLabClient;
use crate::gitlab::transformers::{transform_discussion, transform_notes};
use crate::gitlab::transformers::{NoteableRef, transform_discussion, transform_notes};
use super::issues::IssueForDiscussionSync;
@@ -129,8 +129,11 @@ async fn ingest_discussions_for_issue(
)?;
// Transform and store discussion
let normalized =
transform_discussion(&gitlab_discussion, local_project_id, issue.local_issue_id);
let normalized = transform_discussion(
&gitlab_discussion,
local_project_id,
NoteableRef::Issue(issue.local_issue_id),
);
// Wrap all discussion+notes operations in a transaction for atomicity
let tx = conn.unchecked_transaction()?;
@@ -217,10 +220,10 @@ fn upsert_discussion(
) -> Result<()> {
conn.execute(
"INSERT INTO discussions (
gitlab_discussion_id, project_id, issue_id, noteable_type,
gitlab_discussion_id, project_id, issue_id, merge_request_id, noteable_type,
individual_note, first_note_at, last_note_at, last_seen_at,
resolvable, resolved, raw_payload_id
) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)
) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)
ON CONFLICT(project_id, gitlab_discussion_id) DO UPDATE SET
first_note_at = excluded.first_note_at,
last_note_at = excluded.last_note_at,
@@ -232,6 +235,7 @@ fn upsert_discussion(
&discussion.gitlab_discussion_id,
discussion.project_id,
discussion.issue_id,
discussion.merge_request_id,
&discussion.noteable_type,
discussion.individual_note,
discussion.first_note_at,