Annotate the context commit list with the entity it presents
What does this MR do and why?
GET /projects/:id/merge_requests/:merge_request_iid/context_commits presents its commits with: Entities::CommitWithLink, type: :full, request: merge_request (lib/api/merge_requests.rb:594), while its desc block declares success Entities::Commit (lib/api/merge_requests.rb:581). Grape reads that declaration for the documentation only, so nothing fails, but everything generated from it describes a smaller object than the route sends: both OpenAPI documents point the 200 response at the Commit schema and have no schema for CommitWithLink at all, and doc/api/merge_request_context_commits.md prints an example with twelve of the Commit keys and none of the ten that CommitWithLink adds.
CommitWithLink (lib/api/entities/commit_with_link.rb) is Commit plus ten keys, and this route sends all ten because it passes type: :full: author (a UserPath), author_gravatar_url, commit_url, commit_path, description_html, title_html, signature_html, prev_commit_id, next_commit_id and pipeline_status_path.
The annotation dates from !24054 (merged) (12.8), which pared this route back to Entities::Commit. Two months later !27698 (merged) (12.10) switched the handler to CommitWithLink and left the desc as it was. The POST at the same path is annotated Entities::Commit and presents it, which is right, so I left it alone.
The change:
lib/api/merge_requests.rb: thedescof the list route declaresEntities::CommitWithLink.doc/api/openapi/openapi_v3.yaml: regenerated withbin/rake gitlab:openapi:v3:generate. The200response referencesAPIEntitiesCommitWithLink, and the document gains that component andAPIEntitiesUserPath, which itsauthorreferences. Nothing else in the document changes.doc/api/openapi/openapi_v2.yaml: the same change, the$refand the two definitions, taken from abin/rake gitlab:openapi:v2:generaterun. That run also rewrites about 12,400 unrelated lines, because the v2 document has drifted from the routes onmaster, so I kept only the hunks this route causes.doc/api/merge_request_context_commits.md: the list section gets an example request, and an example response with every key the route sends, built from a response I recorded in a request spec run. It keeps the page's commit SHA and dates. The title loses its backticks, becausetitle_htmlwould render them as Markdown and I only recorded a plain title, and the author is theExample Usertheauthorobject names.spec/requests/api/merge_requests_spec.rb: a new example reads the entity the route declares and expects the keys of the response to be exactly that entity's exposures, so the declaration and the response cannot drift apart again without a failure.
Four of the ten keys are null on every commit this route sends. prev_commit_id, next_commit_id and pipeline_status_path read presenter options the route does not pass. signature_html renders only for a signed commit, and MergeRequestContextCommit#to_commit rebuilds the commit from its stored row with Commit.from_hash, which leaves no Gitaly commit behind it, so Commit#raw_signature_type is nil and Commit#has_signature? is false. Whether those four belong on this route is a separate question, so this MR documents the response as it is and changes nothing about what the route sends.
References
I searched the issues and merge requests of this project for the route, the entity and the context commits API page, and found nothing open about this. The entity name entered the route in !27698 (merged); the annotation was written in !24054 (merged).
One open issue is about the general cause, though it does not name this route:
- Related to #19130, where a note says that because the entity is written twice, in
successand again inpresent ..., with:, thesuccessdeclaration is "more like a comment, which means it can be wrong without consequences". This route is a case of that. The example I added tospec/requests/api/merge_requests_spec.rbmakes this one route's declaration fail a test when it is wrong. I did not take up the issue's proposal of leavingwith:out, which a later note there says would expose all of the object's fields.
The route declares feature_category :code_review_workflow, which belongs to the Code Review group (AI Coding stage). Marc Shaw and Eugenia Grieff reviewed the group's recent backend changes to this API, my !255702 (merged) and !255704 (merged) among them, Patrick Bajao has worked on context commits in the group, and Uma Chandran is the code owner of doc/api/merge_request_context_commits.md, so they seem the right reviewers.
Where this comes from
I maintain gitlab-mcp-server, an MCP server that exposes the GitLab API to AI assistants. It checks the fields it publishes against what GitLab says each route sends, and it read this route's entity from the annotation, so none of the ten keys reached that check. Surfacing the author and rendered title of a context commit meant reading the handler instead, which is where the two disagree. The project keeps a record of what it finds in its dependencies in upstream-bugs.md, and this MR is the change that entry proposes.
Screenshots or screen recordings
Not applicable: the change is to the API's documentation, with no UI.
How to set up and validate locally
- Regenerate the OpenAPI v3 document and check it is current:
bin/rake gitlab:openapi:v3:generate bin/rake gitlab:openapi:v3:check_docsgit diff doc/api/openapi/openapi_v3.yamlis empty. - Read the
200response ofGET /api/v4/projects/{id}/merge_requests/{merge_request_iid}/context_commitsindoc/api/openapi/openapi_v3.yaml: it referencesAPIEntitiesCommitWithLink, whose properties match the keys of a real response. - As a user who can read a merge request with a context commit, with a personal access token that has the
read_apiscope, list the context commits:Every commit in the answer carries the keys of the example oncurl --header "PRIVATE-TOKEN: <your_access_token>" \ --url "http://gdk.test:3000/api/v4/projects/<project_id>/merge_requests/<merge_request_iid>/context_commits"doc/api/merge_request_context_commits.md,authorandcommit_urlamong them. - Run the specs:
bin/rspec spec/requests/api/merge_requests_spec.rb ee/spec/requests/api/merge_requests_spec.rb
Specs and checks I ran
- The new example, against
masterwithout the change tolib/api/merge_requests.rb: it fails, listing as extra elements exactly the ten keys above (author,author_gravatar_url,commit_path,commit_url,description_html,next_commit_id,pipeline_status_path,prev_commit_id,signature_html,title_html). With the change it passes. - With the change,
spec/requests/api/merge_requests_spec.rb: 824 examples, 0 failures, 34 pending;ee/spec/requests/api/merge_requests_spec.rb: 68 examples, 0 failures, 1 pending. Every pending example is one the existingauthorizing granular token permissionsshared examples skip themselves, with "namespace has no top-level group", "public-access bypass only applies to public resources" or "only meaningful on Project/Group boundaries". bin/rake gitlab:openapi:v3:check_docs: up to date.bin/rake gitlab:openapi:v3:validate_tag_docs: valid.bin/rake gitlab:api:check_high_impact_entity_baseline, which counts for each entity the endpoints whose annotation reaches it: the same output with and without the change, so the annotation makes no entity a new high-impact one. On themasterthis branch is based on, that output reportsAPI::Entities::MergeRequestAuthoras a new high-impact entity either way, which this MR does not touch.- RuboCop on the two changed Ruby files: no offenses.
scripts/lint/commit_linter.rbon the commit: no problems. - markdownlint-cli2 and Vale (the
lint-markdownimage the docs pipeline uses) on the changed page: no errors or warnings. Vale makes one suggestion, the page's reading level (8.73 against a target of 8), whichmasteralready has at 8.42.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.