Add note_id support to TodoCreate mutation

Summary

This is the backend half of adding a to-do for a specific comment in a discussion thread on an issue or merge request. It extends the to-do service and the TodoCreate GraphQL mutation so a to-do can reference an individual comment while keeping the parent issue or MR as its target. The change is behind a feature flag and is fully backward compatible with existing callers. The frontend comes in follow-up MRs.


Reviewer note — this is MR 1 of 3 in a stack.

  • !251726 (merged) (this MR) — backend: noteId on the TodoCreate mutation ← review this one
  • !251727 — frontend: per-comment to-do button on issue/MR comments
  • !251848 — frontend: per-comment to-do button on work item comments

To test the full feature end-to-end, check out !251848. That branch includes the whole stack. Instructions are in its description.


What this MR does

  • TodoService#mark_todo accepts an optional note: keyword. When given, the created to-do keeps the parent issue or merge request as its target and also references the specific comment through the existing note_id column. This mirrors how mention to-dos already attach a note.
  • TodoCreate GraphQL mutation gains an optional noteId argument, marked as an experiment. When noteId is provided and the flag is on, the mutation loads the note, checks that it belongs to the target and that the user can read it, then creates the to-do referencing the comment.
  • targetId stays required and behaves exactly as before, so the existing schema and callers are untouched.
  • The pending to-do dedup now includes the note, so a to-do for one comment, a to-do for another comment, and the plain to-do on the issuable are distinct. Without this, an existing pending to-do on the issuable suppressed every comment to-do and the mutation returned a null todo with no errors.

Feature flag

Behind todo_for_comment (type: wip, default disabled). When the flag is off, noteId is ignored entirely: the note is not looked up or validated, and the to-do is created without a note reference, as if the argument did not exist.

Authorization

Reuses the read_note check on the comment plus the existing create_todo check on the target through authorized_find!. The note must belong to the target. A note the user cannot read raises resource-not-available.

Database

No migration. The todos.note_id column and the Todo belongs_to :note association already exist.

Testing

  • RSpec for the service covering the note path and the note-aware dedup (plain plus comment, two comments, exact duplicates).
  • RSpec for the mutation covering: flag on with a valid note, a note that does not belong to the target, a note the user cannot read, flag off ignoring the note, including one the user cannot read.
  • GraphQL schema docs and introspection schemas regenerated.

What it looks like

The frontend halves are !251727 and !251848. With the stack applied, each comment's More actions menu gets an "Add a to-do" entry and the To-Do List shows the comment text:

Comment More actions menu To-Do List
more actions menu on an issue comment todo list
Edited by Angus Ryer

Merge request reports

Loading
Loading