fix: treat a required request body becoming optional as compatible

IN
4h ago
+63-5

Fixes #329

What

Diffing a spec where an operation's request body changed from required: true to optional (required: false or the key removed) now reports a compatible change instead of an incompatible one. The reverse direction, optional to required, stays incompatible.

Why

Making a required request body optional cannot break existing clients: they keep sending the body, and the server must keep accepting it under the new spec. Reporting that as breaking is a false positive that blocks CI pipelines relying on --fail-on-incompatible.

How

ChangedRequestBody.isCoreChanged() now short-circuits to COMPATIBLE when both the old and new request bodies exist and only the required flag decreases, mirroring the directional checks already used by ChangedRequired (schema properties) and ChangedReadOnly:

  • required -> optional: compatible, still listed under "What's Changed" as a non-breaking change
  • optional -> required: incompatible, now gated by the previously unused incompatible.request.body.increased property (REQUEST_BODY_REQUIRED_INCREASED) instead of incompatible.openapi.endpoints.decreased, which the request body check had been borrowing as a placeholder. Wiring the intended property also brings the request body in line with every other backward-incompatibility check, which gates on its own property.
  • a removed request body whose old body was required: still incompatible (both bodies must exist for the compatible short-circuit to apply)

Tests

  • RequestBodyBCTest now covers both directions: requiredIncreased (incompatible when incompatible.request.body.increased is enabled, compatible when disabled) and requiredDecreasedButCompatible (always compatible)
  • RequestDiffTest gains model-level assertions for both directions, checking the operation and spec-level verdicts
  • Full ./mvnw test is green

CLI check

For required: true -> required: false on the same operation:

$ openapi-diff old.yaml new.yaml --state
compatible

and the reverse pair still reports incompatible.


Summary by cubic

Fixes #329: a request body changing from required: true to optional (required: false or the key removed) is now reported as a compatible change instead of an incompatible one. Optional to required stays incompatible.

  • Making a required body optional cannot break existing clients that keep sending the body, so the change shows under "What's Changed" as non-breaking.
  • The optional-to-required check now gates on the intended incompatible.request.body.increased property instead of borrowing incompatible.openapi.endpoints.decreased.
  • A removed request body whose old body was required stays incompatible.