Skip to content

Commit 55dbfef

Browse files
fix(linear): recover owning issue id for comments and history
The GraphQL collector stores the query variables (which carry issueId) in the raw row's input column, but the comment and history extractors parsed it as {"Id":...} (SimpleLinearIssue.Id), so the owning issue id came out empty and the convertor joins produced zero domain comments/changelogs on real data. The e2e fixtures hand-wrote {"Id":...}, masking it. Parse issueId (with an Id fallback) and update the fixtures to the real collector shape. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Eduardo Rodrigues <2961314+eduardoarantes@users.noreply.github.com>
1 parent da340e4 commit 55dbfef

5 files changed

Lines changed: 25 additions & 9 deletions

File tree

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
id,params,data,url,input,created_at
2-
1,"{""ConnectionId"":1,""TeamId"":""team-1""}","{""Id"":""comment-1"",""Body"":""Looking into this"",""CreatedAt"":""2026-05-02T10:00:00Z"",""UpdatedAt"":""2026-05-02T10:00:00Z"",""User"":{""Id"":""user-2""}}",https://api.linear.app/graphql,"{""Id"":""issue-1""}",2026-05-02 10:00:00.000
3-
2,"{""ConnectionId"":1,""TeamId"":""team-1""}","{""Id"":""comment-2"",""Body"":""Fixed in PR 42"",""CreatedAt"":""2026-05-03T09:00:00Z"",""UpdatedAt"":""2026-05-03T09:30:00Z"",""User"":{""Id"":""user-1""}}",https://api.linear.app/graphql,"{""Id"":""issue-1""}",2026-05-03 09:00:00.000
4-
3,"{""ConnectionId"":1,""TeamId"":""team-1""}","{""Id"":""comment-3"",""Body"":""Any update?"",""CreatedAt"":""2026-05-02T11:00:00Z"",""UpdatedAt"":""2026-05-02T11:00:00Z"",""User"":{""Id"":""user-1""}}",https://api.linear.app/graphql,"{""Id"":""issue-2""}",2026-05-02 11:00:00.000
2+
1,"{""ConnectionId"":1,""TeamId"":""team-1""}","{""Id"":""comment-1"",""Body"":""Looking into this"",""CreatedAt"":""2026-05-02T10:00:00Z"",""UpdatedAt"":""2026-05-02T10:00:00Z"",""User"":{""Id"":""user-2""}}",https://api.linear.app/graphql,"{""issueId"":""issue-1""}",2026-05-02 10:00:00.000
3+
2,"{""ConnectionId"":1,""TeamId"":""team-1""}","{""Id"":""comment-2"",""Body"":""Fixed in PR 42"",""CreatedAt"":""2026-05-03T09:00:00Z"",""UpdatedAt"":""2026-05-03T09:30:00Z"",""User"":{""Id"":""user-1""}}",https://api.linear.app/graphql,"{""issueId"":""issue-1""}",2026-05-03 09:00:00.000
4+
3,"{""ConnectionId"":1,""TeamId"":""team-1""}","{""Id"":""comment-3"",""Body"":""Any update?"",""CreatedAt"":""2026-05-02T11:00:00Z"",""UpdatedAt"":""2026-05-02T11:00:00Z"",""User"":{""Id"":""user-1""}}",https://api.linear.app/graphql,"{""issueId"":""issue-2""}",2026-05-02 11:00:00.000
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
id,params,data,url,input,created_at
2-
1,"{""ConnectionId"":1,""TeamId"":""team-1""}","{""Id"":""hist-1"",""CreatedAt"":""2026-05-01T08:00:00Z"",""Actor"":{""Id"":""user-2""},""FromState"":{""Id"":""state-backlog"",""Name"":""Backlog"",""Type"":""backlog""},""ToState"":{""Id"":""state-todo"",""Name"":""Todo"",""Type"":""unstarted""}}",https://api.linear.app/graphql,"{""Id"":""issue-1""}",2026-05-01 08:00:00.000
3-
2,"{""ConnectionId"":1,""TeamId"":""team-1""}","{""Id"":""hist-2"",""CreatedAt"":""2026-05-02T00:00:00Z"",""Actor"":{""Id"":""user-1""},""FromState"":{""Id"":""state-todo"",""Name"":""Todo"",""Type"":""unstarted""},""ToState"":{""Id"":""state-inprogress"",""Name"":""In Progress"",""Type"":""started""}}",https://api.linear.app/graphql,"{""Id"":""issue-1""}",2026-05-02 00:00:00.000
4-
3,"{""ConnectionId"":1,""TeamId"":""team-1""}","{""Id"":""hist-3"",""CreatedAt"":""2026-05-03T00:00:00Z"",""Actor"":{""Id"":""user-1""},""FromState"":{""Id"":""state-inprogress"",""Name"":""In Progress"",""Type"":""started""},""ToState"":{""Id"":""state-done"",""Name"":""Done"",""Type"":""completed""}}",https://api.linear.app/graphql,"{""Id"":""issue-1""}",2026-05-03 00:00:00.000
2+
1,"{""ConnectionId"":1,""TeamId"":""team-1""}","{""Id"":""hist-1"",""CreatedAt"":""2026-05-01T08:00:00Z"",""Actor"":{""Id"":""user-2""},""FromState"":{""Id"":""state-backlog"",""Name"":""Backlog"",""Type"":""backlog""},""ToState"":{""Id"":""state-todo"",""Name"":""Todo"",""Type"":""unstarted""}}",https://api.linear.app/graphql,"{""issueId"":""issue-1""}",2026-05-01 08:00:00.000
3+
2,"{""ConnectionId"":1,""TeamId"":""team-1""}","{""Id"":""hist-2"",""CreatedAt"":""2026-05-02T00:00:00Z"",""Actor"":{""Id"":""user-1""},""FromState"":{""Id"":""state-todo"",""Name"":""Todo"",""Type"":""unstarted""},""ToState"":{""Id"":""state-inprogress"",""Name"":""In Progress"",""Type"":""started""}}",https://api.linear.app/graphql,"{""issueId"":""issue-1""}",2026-05-02 00:00:00.000
4+
3,"{""ConnectionId"":1,""TeamId"":""team-1""}","{""Id"":""hist-3"",""CreatedAt"":""2026-05-03T00:00:00Z"",""Actor"":{""Id"":""user-1""},""FromState"":{""Id"":""state-inprogress"",""Name"":""In Progress"",""Type"":""started""},""ToState"":{""Id"":""state-done"",""Name"":""Done"",""Type"":""completed""}}",https://api.linear.app/graphql,"{""issueId"":""issue-1""}",2026-05-03 00:00:00.000

backend/plugins/linear/tasks/comment_collector.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,23 @@ const RAW_COMMENTS_TABLE = "linear_comments"
3434
// of child resources (comments, history). Its JSON form is stored in the raw
3535
// row's `input` column so extractors can recover the owning issue id.
3636
type SimpleLinearIssue struct {
37-
Id string
37+
// Id is populated by the DalCursorIterator (the _tool_linear_issues.id column)
38+
// when driving per-issue child collection.
39+
Id string `json:"Id"`
40+
// IssueId is populated when parsing a raw row's `input` column: the GraphQL
41+
// collector stores the query variables there (which carry `issueId`), not the
42+
// iterator element. OwningIssueId resolves whichever is present.
43+
IssueId string `json:"issueId" gorm:"-"`
44+
}
45+
46+
// OwningIssueId returns the issue id this child row belongs to, tolerating both
47+
// the iterator element shape ({"Id":...}) and the collector's stored variables
48+
// shape ({"issueId":...}).
49+
func (s SimpleLinearIssue) OwningIssueId() string {
50+
if s.IssueId != "" {
51+
return s.IssueId
52+
}
53+
return s.Id
3854
}
3955

4056
// GraphqlQueryCommentWrapper is the per-issue, paginated `comments` query.

backend/plugins/linear/tasks/comment_extractor.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func ExtractComments(taskCtx plugin.SubTaskContext) errors.Error {
6060
comment := &models.LinearComment{
6161
ConnectionId: data.Options.ConnectionId,
6262
Id: apiComment.Id,
63-
IssueId: issueRef.Id,
63+
IssueId: issueRef.OwningIssueId(),
6464
Body: apiComment.Body,
6565
CreatedAt: apiComment.CreatedAt,
6666
UpdatedAt: apiComment.UpdatedAt,

backend/plugins/linear/tasks/issue_history_extractor.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func ExtractIssueHistory(taskCtx plugin.SubTaskContext) errors.Error {
5959
event := &models.LinearIssueHistory{
6060
ConnectionId: data.Options.ConnectionId,
6161
Id: apiEvent.Id,
62-
IssueId: issueRef.Id,
62+
IssueId: issueRef.OwningIssueId(),
6363
CreatedAt: apiEvent.CreatedAt,
6464
}
6565
if apiEvent.Actor != nil {

0 commit comments

Comments
 (0)