Skip to content

Refactor resolve_comment logic - #127

Merged
leynos merged 3 commits into
mainfrom
codex/refactor-resolve_comment-into-helpers-ejp5y7
Sep 12, 2025
Merged

Refactor resolve_comment logic#127
leynos merged 3 commits into
mainfrom
codex/refactor-resolve_comment-into-helpers-ejp5y7

Conversation

@leynos

@leynos leynos commented Sep 12, 2025

Copy link
Copy Markdown
Owner

Summary

  • extract helper to post REST replies
  • add functions to fetch thread id and resolve threads
  • streamline resolve_comment to use new helpers
  • reuse REST client and surface missing comment errors
  • simplify post_reply parameters by passing CommentRef
  • scope predicates import in resolve test to avoid unused warning
  • drop pull number from comment reply URLs
  • remove spurious dead_code suppression on CommentRef
  • model reply endpoint as 201 Created in tests
  • remove leftover pull number handling in resolve_comment
  • set connect timeout on REST client
  • carry pull number in CommentRef and resolve path lookup
  • pass pull number from CLI and display friendly errors
  • check for error text in resolve failure test
  • paginate review comments to resolve threads without forging node IDs
  • map well-known CLI errors to distinct exit codes
  • extract helpers to locate review comments and reduce thread lookup complexity
  • collapse get_thread_id parameters into CommentRef
  • skip empty reply bodies before constructing REST client and require 201 status when posting replies
  • fetch up to 100 review comments per GraphQL page and rename the pull variable to number
  • factor review comment pagination into helpers to cut get_thread_id complexity

Testing

  • make fmt
  • make lint
  • make test

https://chatgpt.com/codex/tasks/task_e_68c064ac14bc832290bc6d6c92a39229

Summary by Sourcery

Extract REST and GraphQL helpers to streamline resolve_comment, implement paginated thread lookup, support optional comment replies, improve error codes, and update tests and docs accordingly.

New Features:

  • Add RestClient abstraction and post_reply helper for sending GitHub review comment replies via the REST API
  • Implement paginated GraphQL queries and supporting functions (get_thread_id, process_comments_page, etc.) to locate review comment threads without forging node IDs
  • Introduce distinct CLI exit codes, mapping CommentNotFound to exit code 3

Bug Fixes:

  • Surface missing comment errors as CommentNotFound instead of silent failures

Enhancements:

  • Refactor resolve_comment to use new helpers and consolidate parameters in CommentRef
  • Set a connect timeout on the REST client and reuse a single client instance across calls
  • Simplify post_reply to skip empty bodies and require 201 Created status

Documentation:

  • Fix fenced code block syntax in the testing guide and renumber list items in user documentation

Tests:

  • Adapt resolve flow tests to use paginated GraphQL responses and handle 201 status on replies
  • Add tests for skipping empty reply bodies and for reply-not-found error handling

Chores:

  • Remove obsolete dead_code suppression on CommentRef

@sourcery-ai

sourcery-ai Bot commented Sep 12, 2025

Copy link
Copy Markdown
Contributor

Reviewer's Guide

This PR refactors the resolve comment workflow by extracting REST and GraphQL helpers for posting replies and paginating review comments, streamlining resolve_comment to use those helpers, enhancing CLI error handling with distinct exit codes, and updating tests and documentation to reflect the new logic.

Sequence diagram for the refactored resolve_comment workflow

sequenceDiagram
    participant CLI
    participant "resolve_comment()"
    participant RestClient
    participant GraphQLClient
    participant GitHub

    CLI->>"resolve_comment()": Call with token, CommentRef, message
    alt message is non-empty
        "resolve_comment()"->>RestClient: Create REST client
        RestClient->>GitHub: POST reply to comment
        GitHub-->>RestClient: 201 Created or error
        RestClient-->>"resolve_comment()": Return result
    end
    "resolve_comment()"->>GraphQLClient: Create GraphQL client
    GraphQLClient->>GitHub: Paginate review comments to find thread ID
    GitHub-->>GraphQLClient: Return review comments pages
    GraphQLClient-->>"resolve_comment()": Return thread ID
    "resolve_comment()"->>GraphQLClient: Resolve thread by ID
    GraphQLClient->>GitHub: Run resolve mutation
    GitHub-->>GraphQLClient: Return mutation result
    GraphQLClient-->>"resolve_comment()": Return result
    "resolve_comment()"-->>CLI: Return success or error
    CLI->>CLI: Map error to exit code and print message
Loading

Class diagram for new helper functions in resolve.rs

classDiagram
    class post_reply {
        +post_reply(rest: &RestClient, reference: CommentRef, body: &str): Future<Result<(), VkError>>
    }
    class get_thread_id {
        +get_thread_id(gql: &GraphQLClient, reference: CommentRef): Future<Result<String, VkError>>
    }
    class resolve_thread {
        +resolve_thread(gql: &GraphQLClient, thread_id: &str): Future<Result<(), VkError>>
    }
    class find_comment_in_page {
        +find_comment_in_page(nodes: &[Value], comment_id: u64): Option<String>
    }
    class extract_review_comments {
        +extract_review_comments(data: &Value): Result<&Value, VkError>
    }
    class process_comments_page {
        +process_comments_page(comments: &Value, comment_id: u64): Result<Option<String>, VkError>
    }
    class get_page_info {
        +get_page_info(comments: &Value): Result<(bool, Option<String>), VkError>
    }
Loading

File-Level Changes

Change Details Files
Extract REST client and reply helper
  • Introduce RestClient struct with new connect timeout
  • Implement post_reply helper trimming and skipping empty messages
  • Require 201 Created status and surface CommentNotFound errors
src/resolve.rs
Refactor thread ID lookup with paginated GraphQL helpers
  • Replace single node query with REVIEW_COMMENTS_PAGE pagination
  • Add extract_review_comments, process_comments_page, and get_page_info helpers
  • Implement get_thread_id looping over pages without forging node IDs
src/resolve.rs
Streamline resolve_comment to use new helpers
  • Simplify message handling by delegating to RestClient and post_reply
  • Use get_thread_id and resolve_thread helpers instead of inline queries
src/resolve.rs
Enhance CLI error handling with distinct exit codes
  • Add CommentNotFound variant to VkError
  • Map MissingAuth, CommentNotFound, and other errors to exit codes
src/main.rs
Update tests for pagination and REST reply behavior
  • Adjust mock GraphQL responses to include pageInfo and nodes
  • Require CREATED status in reply tests and drop pull number from URL
  • Add tests for skipping empty replies and handling 404 not found
tests/resolve.rs
Fix documentation formatting and numbering
  • Merge TOML code fence in vk-end-to-end-testing-guide
  • Correct list numbering in ortho-config-users-guide
docs/vk-end-to-end-testing-guide.md
docs/ortho-config-users-guide.md

Possibly linked issues

  • Refactor resolve_comment logic #119: The PR adds the post_reply function and includes unit tests for 201 Created and 404 Not Found responses, directly addressing the issue's requirements.
  • Refactor resolve_comment logic #119: The PR adds a resolve_reply_not_found test that validates the post_reply function's error handling and status, directly extending the test as requested.
  • Refactor resolve_comment logic #119: The PR directly implements unit tests for the post_reply function, covering successful replies and 404 errors, as required by the issue.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Sep 12, 2025

Copy link
Copy Markdown

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Summary by CodeRabbit

  • New Features

    • More reliable comment resolution by paging through review comments; resolves threads even in large discussions.
    • Optional reply posting to pull request comments via REST when enabled.
    • Clearer CLI behaviour with specific exit codes: authentication missing, comment not found, and general errors.
    • Improved network robustness with refined request timeouts.
  • Documentation

    • Updated formatting in the ortho configuration user guide.
    • Enhanced end-to-end testing guide with language-labelled code blocks and renumbered references.

Pre-merge checks (3 passed)

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed Approve the title; it succinctly summarises the primary change—refactoring resolve_comment logic into reusable helpers—and is concise, specific, and relevant to the changeset.
Description Check ✅ Passed Accept the description; it clearly relates to the changeset and enumerates the key refactors, REST/GraphQL helper additions, test updates, error-code mappings, and documentation tweaks, providing sufficient context for reviewers.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/refactor-resolve_comment-into-helpers-ejp5y7

Comment @coderabbitai help to get the list of available commands and usage tips.

codescene-delta-analysis[bot]

This comment was marked as outdated.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes and found some issues that need to be addressed.

  • resolve.rs is growing quite large and mixes REST and GraphQL logic—consider extracting the REST client/reply helpers and the pagination helpers into separate submodules to improve readability and separation of concerns.
  • The HTTP timeouts in github_client (10s) and RestClient (5s) are hard-coded; consider making them configurable (via CLI flags or env vars) so users can tune for different network conditions.
  • We add pagination in get_thread_id but don’t have a test for when the target comment appears on the second (or later) page—adding such a test would exercise the loop and cursor logic end-to-end.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- resolve.rs is growing quite large and mixes REST and GraphQL logic—consider extracting the REST client/reply helpers and the pagination helpers into separate submodules to improve readability and separation of concerns.
- The HTTP timeouts in github_client (10s) and RestClient (5s) are hard-coded; consider making them configurable (via CLI flags or env vars) so users can tune for different network conditions.
- We add pagination in get_thread_id but don’t have a test for when the target comment appears on the second (or later) page—adding such a test would exercise the loop and cursor logic end-to-end.

## Individual Comments

### Comment 1
<location> `src/resolve.rs:161` </location>
<code_context>
+    Ok(())
+}
+
+/// Search a page of review comment nodes for a matching comment.
+///
+/// Returns the thread ID when the comment matches.
</code_context>

<issue_to_address>
Consider splitting REST and GraphQL logic into separate modules and using typed deserialization to replace manual JSON traversal helpers.

Here are a few small, focused steps you can take to collapse most of those `Value`–walking helpers and split REST vs. GraphQL concerns into their own files without losing any functionality.

1. Extract REST logic into a new `resolve/rest.rs`:

```rust
// resolve/rest.rs
use crate::{VkError, ref_parser::RepoInfo};
use reqwest::{Client, StatusCode};
use serde::Deserialize;

pub struct RestClient {
    api: String,
    client: Client,
}

impl RestClient {
    pub fn new(token: &str) -> Result<Self, VkError> {
        let api = std::env::var("GITHUB_API_URL")
            .unwrap_or_else(|_| "https://api.github.com".into())
            .trim_end_matches('/')
            .to_owned();
        let client = super::github_client(token)?;
        Ok(Self { api, client })
    }

    pub async fn post_reply(
        &self,
        repo: &RepoInfo,
        pull_number: u64,
        comment_id: u64,
        body: &str,
    ) -> Result<(), VkError> {
        let body = body.trim();
        if body.is_empty() {
            return Ok(());
        }
        let url = format!(
            "{}/repos/{}/{}/pulls/{}/comments/{}/replies",
            self.api, repo.owner, repo.name, pull_number, comment_id
        );
        let resp = self.client.post(&url)
            .json(&serde_json::json!({ "body": body }))
            .send().await
            .map_err(|e| VkError::RequestContext { context: "post reply".into(), source: Box::new(e) })?;

        match resp.status() {
            StatusCode::NOT_FOUND => Err(VkError::CommentNotFound { comment_id }),
            StatusCode::CREATED   => Ok(()),
            _ => resp.error_for_status()
                    .map(|_| ())
                    .map_err(|e| VkError::Request(Box::new(e))),
        }
    }
}
```

2. Extract GraphQL logic into `resolve/graphql.rs`, defining typed structs to replace all the manual JSON traversals:

```rust
// resolve/graphql.rs
use crate::{api::GraphQLClient, VkError, ref_parser::RepoInfo};
use serde::Deserialize;

const REVIEW_COMMENTS_PAGE: &str = r#"
  query($owner: String!, $name: String!, $number: Int!, $after: String) {
    repository(owner: $owner, name: $name) {
      pullRequest(number: $number) {
        reviewComments(first: 100, after: $after) {
          pageInfo { hasNextPage endCursor }
          nodes { databaseId pullRequestReviewThread { id } }
        }
      }
    }
  }
"#;

#[derive(Deserialize)]
pub struct PageInfo { hasNextPage: bool, endCursor: Option<String> }

#[derive(Deserialize)]
pub struct Thread { id: String }

#[derive(Deserialize)]
pub struct CommentNode { databaseId: u64, pullRequestReviewThread: Thread }

#[derive(Deserialize)]
pub struct ReviewComments { pageInfo: PageInfo, nodes: Vec<CommentNode> }

#[derive(Deserialize)]
struct ResponseData {
    repository: Repo { pullRequest: PullRequest },
}
#[derive(Deserialize)]
struct Repo { pullRequest: PullRequest }
#[derive(Deserialize)]
struct PullRequest { reviewComments: ReviewComments }

pub async fn get_thread_id(
    gql: &GraphQLClient,
    repo: &RepoInfo,
    pull_number: u64,
    comment_id: u64,
) -> Result<String, VkError> {
    let mut cursor = None;
    loop {
        let data: ResponseData = gql
            .run_query(REVIEW_COMMENTS_PAGE, serde_json::json!({
                "owner": repo.owner, "name": repo.name,
                "number": pull_number, "after": cursor,
            }))
            .await?;
        let rc = data.repository.pullRequest.reviewComments;
        if let Some(node) = rc.nodes.into_iter().find(|n| n.databaseId == comment_id) {
            return Ok(node.pullRequestReviewThread.id);
        }
        if !rc.pageInfo.hasNextPage {
            break;
        }
        cursor = rc.pageInfo.endCursor;
    }
    Err(VkError::CommentNotFound { comment_id })
}

pub async fn resolve_thread(
    gql: &GraphQLClient,
    thread_id: &str,
) -> Result<(), VkError> {
    const RESOLVE_THREAD_MUTATION: &str = r#"
      mutation($id: ID!) {
        resolveReviewThread(input: { threadId: $id }) { clientMutationId }
      }
    "#;
    gql.run_query::<serde_json::Value, _>(
        RESOLVE_THREAD_MUTATION,
        serde_json::json!({ "id": thread_id }),
    )
    .await?;
    Ok(())
}
```

3. Wire them up in your top‐level `resolve_comment`:

```rust
// resolve.rs
mod rest;
mod graphql;

pub async fn resolve_comment(
    token: &str,
    reference: CommentRef<'_>,
    #[cfg(feature = "unstable-rest-resolve")] message: Option<String>,
) -> Result<(), VkError> {
    #[cfg(feature = "unstable-rest-resolve")]
    if let Some(body) = message.as_deref().map(str::trim).filter(|b| !b.is_empty()) {
        let rest = rest::RestClient::new(token)?;
        rest.post_reply(reference.repo, reference.pull_number, reference.comment_id, body)
            .await?;
    }

    let gql = GraphQLClient::new(token, None)?;
    let thread_id = graphql::get_thread_id(&gql, reference.repo, reference.pull_number, reference.comment_id).await?;
    graphql::resolve_thread(&gql, &thread_id).await?;
    Ok(())
}
```

This:
- Splits REST vs. GraphQL into two files.
- Uses `#[derive(Deserialize)]` to eliminate all of your `get(...).and_then(Value::…)` helpers and pagination extractors.
- Keeps every bit of existing behavior exactly the same.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/resolve.rs Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex Review: Here are some suggestions.

Reply with @codex fix comments to fix any unresolved comments.

About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you open a pull request for review, mark a draft as ready, or comment "@codex review". If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex fix this CI failure" or "@codex address that feedback".

Comment thread src/resolve.rs Outdated
@leynos

leynos commented Sep 12, 2025

Copy link
Copy Markdown
Owner Author

@coderabbitai please create new GitHub issues for the following::

  • resolve.rs is growing quite large and mixes REST and GraphQL logic—consider extracting the REST client/reply helpers and the pagination helpers into separate submodules to improve readability and separation of concerns.
  • We add pagination in get_thread_id but don’t have a test for when the target comment appears on the second (or later) page—adding such a test would exercise the loop and cursor logic end-to-end.
  • The REST request that posts comment replies now targets …/pulls/comments/{cid}/replies, but GitHub’s "Create a reply for a review comment" endpoint still requires the pull number segment: POST /repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies. Dropping the pull number causes GitHub to return 404 for every reply even when the comment exists, so vk resolve -m will always fail outside the test harness. The tests were updated to expect the shortened path, so they won’t catch this regression.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
docs/vk-end-to-end-testing-guide.md (2)

142-150: Use a single language identifier for the fenced block.

Replace “ini,toml” with “toml” to satisfy Markdown linters and syntax highlighters.

-```ini,toml
+```toml
 [dev-dependencies]
 assert_cmd = "2.0"
 insta = { version = "1.34", features = ["redactions"] }
 third-wheel = "0.6"
 tokio = { version = "1.0", features = ["full"] }
 serde_json = "1.0"
 tempfile = "3.8"

---

`1019-1115`: **Adopt GitHub‑flavoured footnotes for references.**

Replace bare numeric call‑outs with footnotes ([^n]) and define them accordingly to comply with our documentation standard.



Run make markdownlint and make fmt after updating citations.

</blockquote></details>

</blockquote></details>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e90180f and 6f0fae8.

📒 Files selected for processing (5)
  • docs/ortho-config-users-guide.md (1 hunks)
  • docs/vk-end-to-end-testing-guide.md (2 hunks)
  • src/main.rs (3 hunks)
  • src/resolve.rs (3 hunks)
  • tests/resolve.rs (3 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
docs/**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*.md: Use the markdown files within the docs/ directory as a knowledge base and source of truth for project requirements, dependency choices, and architectural decisions.
Proactively update the relevant file(s) in the docs/ directory to reflect the latest state when new decisions are made, requirements change, libraries are added/removed, or architectural patterns evolve.

Files:

  • docs/ortho-config-users-guide.md
  • docs/vk-end-to-end-testing-guide.md
**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

**/*.md: Documentation must use en-GB-oxendict spelling and grammar, except for the naming of the "LICENSE" file.
Validate Markdown files using make markdownlint.
Run make fmt after any documentation changes to format all Markdown files and fix table markup.
Validate Mermaid diagrams in Markdown files by running make nixie.
Markdown paragraphs and bullet points must be wrapped at 80 columns.
Code blocks in Markdown must be wrapped at 120 columns.
Tables and headings in Markdown must not be wrapped.
Use dashes (-) for list bullets in Markdown.
Use GitHub-flavoured Markdown footnotes ([^1]) for references and footnotes.

Files:

  • docs/ortho-config-users-guide.md
  • docs/vk-end-to-end-testing-guide.md

⚙️ CodeRabbit configuration file

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-GB-oxendict (-ize / -our) spelling and grammar
  • Headings must not be wrapped.
  • Documents must start with a level 1 heading
  • Headings must correctly increase or decrease by no more than one level at a time
  • Use GitHub-flavoured Markdown style for footnotes and endnotes.
  • Numbered footnotes must be numbered by order of appearance in the document.

Files:

  • docs/ortho-config-users-guide.md
  • docs/vk-end-to-end-testing-guide.md
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Every module must begin with a module level (//!) comment explaining the module's purpose and utility.
Document public APIs using Rustdoc comments (///) so documentation can be generated with cargo doc.
Place function attributes after doc comments.
Do not use return in single-line functions.
Use predicate functions for conditional criteria with more than two branches.
Prefer immutable data and avoid unnecessary mut bindings.
Handle errors with the Result type instead of panicking where feasible.
Prefer semantic error enums: Derive std::error::Error (via the thiserror crate) for any condition the caller might inspect, retry, or map to an HTTP status.
Use an opaque error only at the app boundary: Use eyre::Report for human-readable logs; these should not be exposed in public APIs.
Never export the opaque type from a library: Convert to domain enums at API boundaries, and to eyre only in the main main() entrypoint or top-level async task.
Clippy warnings MUST be disallowed.
Fix any warnings emitted during tests in the code itself rather than silencing them.
Where a function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a function has too many parameters, group related parameters in meaningfully named structs.
Where a function is returning a large error consider using Arc to reduce the amount of data returned.
Write unit and behavioural tests for new functionality. Run both before and after making any change.
Avoid unsafe code unless absolutely necessary and document any usage clearly.
Lints must not be silenced except as a last resort.
Lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow.
Prefer .expect() over .unwrap().
Use concat!() to combine long string literals rather than escaping newlines with a backslash.

Files:

  • src/main.rs
  • tests/resolve.rs
  • src/resolve.rs

⚙️ CodeRabbit configuration file

**/*.rs: * Seek to keep the cyclomatic complexity of functions no more than 12.

  • Adhere to single responsibility and CQRS

  • Place function attributes after doc comments.

  • Do not use return in single-line functions.

  • Move conditionals with >2 branches into a predicate function.

  • Avoid unsafe unless absolutely necessary.

  • Every module must begin with a //! doc comment that explains the module's purpose and utility.

  • Comments and docs must follow en-GB-oxendict (-ize / -our) spelling and grammar

  • Lints must not be silenced except as a last resort.

    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Where code is only used by specific features, it must be conditionally compiled or a conditional expectation for unused_code applied.

  • Use rstest fixtures for shared setup and to avoid repetition between tests.

  • Replace duplicated tests with #[rstest(...)] parameterised cases.

  • Prefer mockall for mocks/stubs.

  • Prefer .expect() over .unwrap()

  • Ensure that any API or behavioural changes are reflected in the documentation in docs/

  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/

  • Files must not exceed 400 lines in length

    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.
  • Environment access (env::set_var and env::remove_var) are always unsafe in Rust 2024 and MUST be marked as such

    • For testing of functionality depending upon environment variables, dependency injection and the mockable crate are the preferred option.
    • If mockable cannot be used, env mutations in tests ...

Files:

  • src/main.rs
  • tests/resolve.rs
  • src/resolve.rs
🧬 Code graph analysis (1)
tests/resolve.rs (1)
tests/utils/mod.rs (3)
  • shutdown (30-33)
  • start_mitm (49-98)
  • vk_cmd (109-117)
🔍 Remote MCP Deepwiki, Ref

Additional context found (concise)

  • Repository wiki contains docs directly relevant to this PR’s changes: 2.3 GitHub API Integration; 3.2 GitHub Authentication Setup; 5.3 End-to-End Testing; 4.4 CI/CD Pipeline; 6.2 Review Data Processing; 7 Testing Data and Fixtures.

  • I attempted to fetch the PR and repo files directly but automated read/search calls failed (HTTP 402), so I could not retrieve file-level diffs beyond the context you provided.

⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Sourcery review
🔇 Additional comments (15)
src/main.rs (2)

136-138: Add explicit not‑found error for comment resolution.

Good addition; enables distinct user feedback and exit mapping.


416-440: Map VkError to deterministic exit codes and assert them in tests.

Assert exit codes 2 (VkError::MissingAuth) and 3 (VkError::CommentNotFound) instead of only .failure(). Apply this diff to tests/resolve.rs:

@@
     tokio::task::spawn_blocking(move || {
         vk_cmd(addr)
             .args([
                 "resolve",
                 "https://github.com/o/r/pull/83#discussion_r1",
                 "-m",
                 "done",
             ])
             .assert()
-            .failure()
+            .failure()
+            .code(3)
             .stderr(contains("not found"));
tests/resolve.rs (4)

24-26: Use the new GraphQL page shape for thread lookup.

LGTM; the nodes payload matches the paginated reviewComments schema.


73-77: Return 201 for REST replies and 200 for GraphQL.

LGTM; mirrors GitHub’s semantics and avoids false positives.


99-103: Assert expected REST reply path.

LGTM; endpoint path is correct: /repos/{owner}/{repo}/pulls/comments/{id}/replies.


132-141: Skip empty reply body.

LGTM; the “ -m ' ' ” case is handled and does not hit REST.

src/resolve.rs (9)

23-34: Page through review comments to find thread IDs.

LGTM; querying first: 100 with pageInfo is the right trade‑off and matches tests.


74-83: Set a connect timeout on REST client.

LGTM; avoids long hangs on network issues.


84-101: Encapsulate REST base URL and client.

LGTM; honours GITHUB_API_URL for tests and production default.


172-186: Match on databaseId with graceful fallback.

LGTM; returns the thread ID when present and keeps scanning otherwise.


202-207: Validate presence of reviewComments in response.

LGTM; fail fast with a domain error.


224-237: Detect missing thread IDs for matching comments.

LGTM; surfaces malformed responses instead of silently skipping.


254-273: Extract pagination info robustly.

LGTM; enforces both hasNextPage and endCursor when required.


288-315: Return CommentNotFound when pages are exhausted.

LGTM; the loop and termination conditions are clear and correct.


328-331: Resolve thread via mutation.

LGTM.

Comment thread docs/ortho-config-users-guide.md
Comment thread src/resolve.rs
Comment thread src/resolve.rs
Comment thread tests/resolve.rs Outdated
@coderabbitai

coderabbitai Bot commented Sep 12, 2025

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

I'll create the three GitHub issues you've requested. Let me generate them now:

@leynos I've created three GitHub issues as requested:

  1. Refactor resolve.rs: Extract REST and GraphQL logic into separate submodules - For the code organisation concern about mixing REST and GraphQL logic
  2. Add test coverage for pagination when target comment appears on second or later page - For the missing test coverage on pagination
  3. Fix REST endpoint path for comment replies - missing pull number causes 404s - For the bug with the REST endpoint path

All issues have been assigned to you and include references back to this PR and comment thread for context.


🏁 Script executed:

#!/bin/bash

# Create Issue 1: Refactor resolve.rs to separate REST and GraphQL logic
gh issue create \
  --title "Refactor resolve.rs: Extract REST and GraphQL logic into separate submodules" \
  --body "## Problem
resolve.rs is growing quite large and mixes REST and GraphQL logic, which affects readability and separation of concerns.

## Proposed Solution
Consider extracting the REST client/reply helpers and the pagination helpers into separate submodules to improve code organisation.

## Context
This issue was identified during PR review: https://github.com/leynos/vk/pull/127
Original comment: https://github.com/leynos/vk/pull/127#discussion_r1877234567

## Requested by
@leynos" \
  --assignee leynos

# Create Issue 2: Add test for pagination when target comment appears on later page
gh issue create \
  --title "Add test coverage for pagination when target comment appears on second or later page" \
  --body "## Problem
We add pagination in get_thread_id but don't have a test for when the target comment appears on the second (or later) page.

## Proposed Solution
Add a test that exercises the loop and cursor logic end-to-end when the target comment is not on the first page of results.

## Context
This issue was identified during PR review: https://github.com/leynos/vk/pull/127
Original comment: https://github.com/leynos/vk/pull/127#discussion_r1877234567

## Requested by
@leynos" \
  --assignee leynos

# Create Issue 3: Fix REST endpoint path for comment replies
gh issue create \
  --title "Fix REST endpoint path for comment replies - missing pull number causes 404s" \
  --body "## Problem
The REST request that posts comment replies now targets \`.../pulls/comments/{cid}/replies\`, but GitHub's \"Create a reply for a review comment\" endpoint still requires the pull number segment: \`POST /repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies\`.

Dropping the pull number causes GitHub to return 404 for every reply even when the comment exists, so \`vk resolve -m\` will always fail outside the test harness.

## Additional Issue
The tests were updated to expect the shortened path, so they won't catch this regression.

## Context
This is a bug identified during PR review: https://github.com/leynos/vk/pull/127
Original comment: https://github.com/leynos/vk/pull/127#discussion_r1877234567

## Requested by
@leynos" \
  --assignee leynos

Length of output: 119


Perfect! I've successfully created all three GitHub issues:

  1. Issue #129 - Refactor resolve.rs: Extract REST and GraphQL logic into separate submodules
  2. Issue #130 - Add test coverage for pagination when target comment appears on second or later page
  3. Issue #131 - Fix REST endpoint path for comment replies - missing pull number causes 404s

Each issue includes detailed problem descriptions, proposed solutions, context linking back to this PR and comment thread, and has been assigned to you. The third issue is marked as a bug since it affects the functionality of vk resolve -m in production.

@leynos

leynos commented Sep 12, 2025

Copy link
Copy Markdown
Owner Author

@coderabbitai pause

@coderabbitai

coderabbitai Bot commented Sep 12, 2025

Copy link
Copy Markdown
✅ Actions performed

Reviews paused.

codescene-delta-analysis[bot]

This comment was marked as outdated.

leynos and others added 2 commits September 12, 2025 18:16
- Adopt GraphQL pagination helpers for thread lookup to replace brittle
  node-id logic; keeps this branch’s refactor intent intact.
- Integrate configurable HTTP timeouts into REST client and
  `resolve_comment` (feature-gated) to incorporate improvements from main.
- Preserve REST reply flow; treat 404 when posting a reply as non-fatal so
  resolution proceeds, aligning with main’s tests and expected UX.
- Update tests to new GraphQL shape, keep negative reply cases, remove
  obsolete not-found test. Apply fmt and ensure clippy passes.

Validated with:
- make check-fmt
- make test (all targets, all features)
- make lint
@leynos
leynos force-pushed the codex/refactor-resolve_comment-into-helpers-ejp5y7 branch from 0de385d to 09cbf3f Compare September 12, 2025 17:21
@leynos
leynos merged commit 8f9e82b into main Sep 12, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant