Skip to content

fix: mark removed schema properties as backward incompatible - #941

Open
Inf1mum wants to merge 1 commit into
OpenAPITools:masterfrom
Inf1mum:fix/673-property-compat
Open

Inf1mum wants to merge 1 commit into
OpenAPITools:masterfrom
Inf1mum:fix/673-property-compat

Conversation

@Inf1mum

@Inf1mum Inf1mum commented Nov 15, 2026 •

Copy link
Copy Markdown

What

Removing a property from a request or response schema is now reported as backward incompatible. Adding properties remains backward compatible.

The verdict is gated by two new config flags (both enabled by default), so it can be tuned per direction like the other schema rules:

  • incompatible.request.properties.decreased (REQUEST_PROPERTIES_DECREASED)
  • incompatible.response.properties.decreased (RESPONSE_PROPERTIES_DECREASED)

Removal of a previously required response property is now governed by RESPONSE_PROPERTIES_DECREASED. RESPONSE_REQUIRED_DECREASED is unchanged where it is still meaningful: a property that keeps existing but moves from required to optional in a response.

Why

Previously only removing a required property from a response schema was incompatible. Removing an optional property - or any property from a request schema - was reported as backward compatible, so clients depending on the deleted property went unnoticed.

For renames, no rename heuristic is attempted (too risky to guess intent); a renamed property surfaces as the missing old property, which is now incompatible, so a rename is no longer silently reported as backward compatible.

Fixes #673, and addresses the schema-property part of #611 (metadata/description changes being reported as compatible is a separate topic). Related context: #724 discusses configurable compatibility rules, #264/#490 already confirmed that adding optional properties must stay compatible (kept as is), and #940 applied the same directional gating pattern to request-body requiredness.

How

ChangedSchema.compatibleForRequest() / compatibleForResponse() now check missingProperties through the new BackwardIncompatibleProp entries, mirroring the directional pattern already used by ChangedEnum (REQUEST_ENUM_DECREASED) and ChangedOneOfSchema (REQUEST_ONEOF_DECREASED / RESPONSE_ONEOF_INCREASED).

Notes on scope:

  • Properties that are readOnly in a request context or writeOnly in a response context were already filtered out of missingProperties by SchemaDiffResult.filterProperties, so they remain unflagged.
  • A discriminator property-name change short-circuits the property diff in ComposedSchemaDiffResult, so that path is unaffected.

Tests:

  • SchemaBCTest: flag tests for request property removal, response property removal, response required-property removal, plus response property addition (compatible).
  • SchemaDiffTest: model tests - property removed -> incompatible, property added -> compatible, required property removed -> incompatible. testAnyOfDiff/testAllOfDiff now expect incompatible because their fixtures remove a property, and addPropertyHandlingTest compares the pair in the direction that actually adds the property (its fixtures date back to the module split; 1 -> 2 was a removal).
  • Several BC fixtures (bc_schema_pattern_changed, bc_schema_type_changed, bc_request_schema_props_put_increased, bc_response_schema_props_required_decreased, bc_schema_changed_but_compatible) incidentally dropped optional properties from Widget; those incidental removals are now incompatible, so the fixtures were cleaned up to keep testing only what they meant to test.
  • AsciidocRenderTest.validateAsciiDocChangeFile asserts the exact render output for a removed-property spec; the expected output now shows Broken compatibility, the missing property line, and the WARNING footer.

CLI check on a minimal spec pair (GET/POST returning a schema with foo):

  • removing foo: Schema: Broken compatibility, Missing property: foo (string), exit code 1 with --fail-on-incompatible (was Backward compatible, exit 0)
  • renaming foo -> bar: reported via Missing property: foo, exit 1
  • adding bar: API changes are backward compatible, exit 0
  • both new flags set to false via --config-file: back to Backward compatible, exit 0

Summary by cubic

Removing a property from a request or response schema is now reported as backward incompatible, so clients that depend on the deleted property no longer go unnoticed. Previously only removing a required response property was flagged; removing optional properties or any request property was reported as compatible.

Adding properties stays compatible, and no rename heuristic is attempted, so renames surface as missing properties.

  • New config flags REQUEST_PROPERTIES_DECREASED and RESPONSE_PROPERTIES_DECREASED gate the verdict per direction, both enabled by default.
  • RESPONSE_REQUIRED_DECREASED now only covers properties that remain but move from required to optional.

Fixes #673 and addresses the schema-property part of #611.

Written for commit bf58312. Summary will update on new commits.

View guided diff

What:
- removing a property from a request or response schema is now reported
  as backward incompatible, gated by two new config flags enabled by
  default: incompatible.request.properties.decreased
  (REQUEST_PROPERTIES_DECREASED) and
  incompatible.response.properties.decreased
  (RESPONSE_PROPERTIES_DECREASED)
- adding properties stays backward compatible
- removal of a previously required response property is now governed by
  RESPONSE_PROPERTIES_DECREASED; RESPONSE_REQUIRED_DECREASED continues
  to cover required -> optional changes (property still present)

Why:
- previously only removing a required property from a response schema
  was incompatible; removing an optional property (or any property in a
  request context) was reported as compatible, so clients depending on
  the deleted property went unnoticed. Renames surface as the missing
  old property, since no rename heuristic is attempted (Fixes OpenAPITools#673,
  addresses OpenAPITools#611)

How:
- ChangedSchema.compatibleForRequest/compatibleForResponse now check
  missingProperties through the new BackwardIncompatibleProp entries,
  mirroring the directional pattern used by ChangedEnum and
  ChangedOneOfSchema; readOnly properties in requests and writeOnly
  properties in responses are already filtered out of
  missingProperties, so they remain unflagged
- SchemaBCTest gains flag tests for request/response property removal,
  SchemaDiffTest gains model tests (removed -> incompatible, added ->
  compatible, required removed -> incompatible), fixtures that
  incidentally removed properties were cleaned up, and the expected
  AsciidocRenderTest output for a removed property now shows the
  incompatible verdict

Signed-off-by: Inf1mum <vadimlazuk5@gmail.com>

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 19 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSchema.java">

<violation number="1" location="core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSchema.java:227">
P2: Removing a previously required response property is still controlled by `RESPONSE_REQUIRED_DECREASED` through `ChangedRequired`, even when `RESPONSE_PROPERTIES_DECREASED` is disabled. Exclude removed property names from the required-decrease check so that flag applies only when the property remains and becomes optional.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Re-trigger cubic

// depend on receiving them (issue #673). This also covers previously
// required properties, which used to be handled here via
// RESPONSE_REQUIRED_DECREASED (still used for required -> optional).
if (!missingProperties.isEmpty() && RESPONSE_PROPERTIES_DECREASED.enabled(context)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Removing a previously required response property is still controlled by RESPONSE_REQUIRED_DECREASED through ChangedRequired, even when RESPONSE_PROPERTIES_DECREASED is disabled. Exclude removed property names from the required-decrease check so that flag applies only when the property remains and becomes optional.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSchema.java, line 227:

<comment>Removing a previously required response property is still controlled by `RESPONSE_REQUIRED_DECREASED` through `ChangedRequired`, even when `RESPONSE_PROPERTIES_DECREASED` is disabled. Exclude removed property names from the required-decrease check so that flag applies only when the property remains and becomes optional.</comment>

<file context>
@@ -214,12 +220,12 @@ private boolean compatibleForResponse() {
+        // depend on receiving them (issue #673). This also covers previously
+        // required properties, which used to be handled here via
+        // RESPONSE_REQUIRED_DECREASED (still used for required -> optional).
+        if (!missingProperties.isEmpty() && RESPONSE_PROPERTIES_DECREASED.enabled(context)) {
+          return false;
         }
</file context>

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.

Deleted or renamed properties are marked as backwards compatible

1 participant