| 1 | # frozen_string_literal: true |
| 2 | |
| 3 | # Posts comments on issues or pull requests. Routing all comment creation through |
| 4 | # here keeps authorization and validation consistent across callers. |
| 5 | class CommentService |
| 6 | class Error < StandardError; end |
| 7 | class NotFound < Error; end |
| 8 | |
| 9 | # Posts a comment authored by +author+ on the issue or pull request numbered |
| 10 | # +number+ in +repository+. Issues and PRs share a number space, so the number |
| 11 | # resolves to exactly one. Raises NotFound when nothing matches and |
| 12 | # ActiveRecord::RecordInvalid on validation failure. Read access to the repo is |
| 13 | # enforced by the caller (the MCP layer only resolves repos the user can read). |
| 14 | def self.create(repository:, author:, number:, body:) |
| 15 | target = repository.issues.find_by(number: number) || |
| 16 | repository.pull_requests.find_by(number: number) |
| 17 | raise NotFound, "No issue or pull request ##{number} in #{repository.full_name}." unless target |
| 18 | |
| 19 | target.comments.create!(user: author, body: body) |
| 20 | end |
| 21 | end |