feat: emit parameter-level annotations

What does this MR do and why?

Implements the add-parameter-extensions OpenSpec change from the previous MR in the stack.

  • The new option: config.parameter_annotations, a Hash defaulting to {}. It maps keys of a param's documentation: hash to OpenAPI vendor extensions. Nothing changes for callers that don't set it.

  • Where extensions appear: on query and path parameter objects, after their standard keys. That includes:

    • bracketed filter[x] parameters, each from its own documentation:;
    • params from with-wrapped helpers, where a param's own value wins;
    • path parameters borrowed from a sibling route.

    They also appear on request-body properties at any depth. On an Array param the extension sits on the property, not its items.

  • How values are written: see the previous MR. Symbols become Strings, Hash keys become Symbols and nil adds nothing. Any other value is dropped, with a dropped parameter annotation warning when config.warnings is on.

  • One shared reader for both paths: a single concern, Concerns::ParameterAnnotatable. On the query/path side it's read only after ParameterConverter#convert's early returns, so body params and hidden params never warn twice or by mistake.

  • Docs: README gets a new "Parameter annotations" section. The table now includes the new option, and an unterminated code fence in the existing "Annotations" example is closed.

gitlab-org/gitlab impact

On release: none. I compared the monolith's bin/rake gitlab:openapi:v3:generate output with the gem on main and on this branch, with the monolith configuration unchanged. openapi_v3.yaml, stdout and stderr were byte-identical, and stderr was empty on both sides.

With annotations in use, I tried a local-only edit in the monolith:

  • set config.parameter_annotations = { tier: 'x-gitlab-tier' };
  • added documentation: { tier: :premium } to weight in ee/lib/ee/api/helpers/issues_helpers.rb (issues_stats_params_ee).

The only change to the document was 6 added lines: x-gitlab-tier: premium on the weight query parameter of the issues list and statistics operations. Those are getApiV4Issues, getApiV4IssuesStatistics, getApiV4GroupsIdIssues, getApiV4GroupsIdIssuesStatistics, getApiV4ProjectsIdIssues and getApiV4ProjectsIdIssuesStatistics.

Before (GET /api/v4/issues):

      - name: weight
        required: false
        description: The weight of the issue
        schema:
          oneOf:
          - type: integer
            nullable: true
          - type: string
            nullable: true
        in: query

After:

      - name: weight
        required: false
        description: The weight of the issue
        schema:
          oneOf:
          - type: integer
            nullable: true
          - type: string
            nullable: true
        in: query
        x-gitlab-tier: premium

How to verify

bundle exec rspec        # 1077 examples, 0 failures on Grape 3.2.1 and 2.4.0
bundle exec rubocop
npx -y @fission-ai/openspec@1.13.2 validate --all --strict

Before merging

  • Run /opsx:archive on this branch, so the MR carries the updated openspec/specs/ (see CLAUDE.md).

Closes #32

Stack

  1. !68 (merged): OpenSpec baseline (docs/openspec-baseline → main)
  2. !69 (merged): Specification for #32 (32-specify-parameter-annotations → docs/openspec-baseline)
  3. !70: Implementation for #32 (32-support-parameter-annotations → 32-specify-parameter-annotations) (this MR)

Merge from the bottom up: !68 (merged), then !69 (merged), then !70.

🤖 Generated with Claude Code

Edited by Max Woolf

Merge request reports

Loading
Loading