Skip to content

feat(ipa): add IPA-132 long-running operation initiation rules - #1448

Merged
julius-jogela merged 3 commits into
mainfrom
feat-ipa-132-lro-initiation
Aug 28, 2026
Merged

julius-jogela merged 3 commits into
mainfrom
feat-ipa-132-lro-initiation

Conversation

@julius-jogela

@julius-jogela julius-jogela commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

Proposed changes

This PR adds the three IPA-132 rules validating how a long-running operation is initiated: 202 Accepted with a Location header carrying the operation handle, no other success status code, and no response body.

Jira ticket: CLOUDP-435358

Rules added

All at warn severity, given $.paths[*][post,put,patch,delete]:

Rule Checks
xgen-IPA-132-lro-initiation-must-return-202-accepted-with-location The 202 response declares a Location header, using that exact casing
xgen-IPA-132-lro-must-not-return-success-status-code-other-than-202 No 2xx response other than 202 is declared, including wildcard codes such as 2XX — blocks the "200 if fast, 202 if slow" pattern
xgen-IPA-132-lro-202-response-must-not-include-content No media type of the 202 response declares a schema; media types carrying only versioning metadata (x-xgen-version) are allowed, consistent with the IPA-108 delete-response precedent

How long-running operations are selected

The rules select on the declared 202 response, through a single isNonLegacyLongRunningOperation helper. The x-xgen-long-running-operation extension is derived from the 202 during the merge step, so selecting on the 202 gives identical results on the federated spec — while also working on the raw per-service specs, where the extension does not exist and where the validation feedback reaches API authors. Legacy operations that predate IPA-132 are excluded, whether marked legacy: true by the merge step or matched by the shared legacy operationId list from #1447.

The shared list becomes a plain JavaScript module (legacyLroOperationIds.js) that the rules import directly, replacing #1447's JSON file: Spectral bundles rule functions with a rollup that cannot import JSON, so a data module is the only bundler-safe carrier. go generate extracts the Go copy from that module instead; the existing Go-side drift test keeps the two in sync.

Testing

  • Per rule: valid, invalid (exact error count, message and path), violation-with-exception (suppressed) and compliant-with-exception (flagged as unnecessary) cases; selection cases covering untagged operations declaring a 202 (violating and valid), operations tagged without a 202 (ignored), synchronous operations (ignored), and legacy exclusion via both the legacy: true marking and the shared operationId list. Unit tests for isNonLegacyLongRunningOperation and the shared-list wiring.
  • Full suite: 132 suites / 846 tests pass. gen-ipa-docs, prettier and eslint clean.
  • spectral lint openapi/.raw/v2.yaml with the full ruleset: 0 xgen-IPA-132-* findings — all 17 legacy 202 operations are excluded by the shared list on the untagged spec.
  • Go side: go build, go test -race, golangci-lint (v2.10.1) clean; both generator outputs verified idempotent.

Checklist

  • I have signed the MongoDB CLA
  • I have added tests that prove my fix is effective or that my feature works

@julius-jogela
julius-jogela force-pushed the feat-ipa-132-lro-initiation branch 2 times, most recently from 1fdf810 to d0e4202 Compare August 24, 2026 15:11
Add the three rules validating how a long-running operation is
initiated. The rules select on the declared 202 response through a
single isCompliantLongRunningOperation helper: the
x-xgen-long-running-operation extension is derived from the 202 during
the merge step, so selecting on the 202 gives the same result on the
federated spec while also working on the per-service source specs the
merge step has not processed. Legacy operations that predate IPA-132
are excluded, whether marked legacy: true by the merge step or matched
by the shared legacy operationId list.

The shared list becomes a plain JavaScript module the rules import
directly, replacing the JSON file: Spectral bundles rule functions
with a rollup that cannot import JSON. go generate now extracts the
Go copy from that module instead.

- xgen-IPA-132-lro-initiation-must-return-202-accepted-with-location:
  the 202 response must declare a Location header, compared
  case-insensitively.
- xgen-IPA-132-lro-must-not-return-success-status-code-other-than-202:
  no other 2xx response may be declared, including wildcard codes such
  as 2XX.
- xgen-IPA-132-lro-202-response-must-not-include-content: no media type
  of the 202 response may declare a schema; media types carrying only
  versioning metadata are allowed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@julius-jogela
julius-jogela force-pushed the feat-ipa-132-lro-initiation branch from d0e4202 to ed4d06d Compare August 24, 2026 15:48
Comment thread tools/spectral/ipa/rulesets/functions/utils/longRunningOperations.js Outdated
Comment thread tools/spectral/ipa/rulesets/functions/utils/longRunningOperations.js Outdated
Rename isCompliantLongRunningOperation to isNonLegacyLongRunningOperation:
the predicate selects operations in scope for IPA-132, it does not assert
that they already follow the contract.

Require the canonical Location casing on the 202 response instead of
matching header names case-insensitively, with a distinct message when a
case variant is declared. The spec has no Location response header today
and existing headers already use canonical casing, so this adds no
findings.
@julius-jogela
julius-jogela merged commit 16f8b15 into main Aug 28, 2026
14 checks passed
@julius-jogela
julius-jogela deleted the feat-ipa-132-lro-initiation branch August 28, 2026 12:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants