-
Notifications
You must be signed in to change notification settings - Fork 88
Description
Overview
Add changelog and breaking change detection rules for OpenAPI 3.1 specific features to the checker package.
Depends on: PR #791 (diff support for OpenAPI 3.1 fields)
Architecture
The diff layer (PR #791) already produces diffs for all 3.1 fields. This issue is about the checker layer that interprets those diffs as breaking/non-breaking changes.
Each checker function has the signature:
func XxxCheck(diffReport *diff.Diff, operationsSources *diff.OperationsSourcesMap, config *Config) ChangesFor each new rule: define a change ID constant, add a localization message in `messages.yaml`, implement the checker, register it in `rules.go`, and add tests.
Priority 1 — Critical
1.1 Handle nullable as type array
Problem: In 3.0, nullable is `nullable: true`. In 3.1, it's `type: ["string", "null"]`. The existing nullable checkers use `NullableDiff` which won't fire for 3.1 type arrays. Meanwhile, the existing type-change checkers (`TypeDiff`, `StringsDiff`) will see adding/removing `"null"` in the type array as a type change rather than a nullable change.
Approach: Modify the type-change checkers to filter out `"null"` from `TypeDiff.Added`/`TypeDiff.Deleted` and instead emit nullable change IDs. Key files:
- `check_request_property_type_changed.go` — filter "null" from TypeDiff, emit `request-property-became-nullable` / `request-property-became-not-nullable`
- `check_response_property_type_changed.go` — same for response
- `check_request_property_became_not_nuallable.go` — extend to also check TypeDiff for "null" addition/removal
- `check_response_property_became_nuallable.go` — same for response
- `check_types.go` — `isTypeContained()` needs to ignore "null" in type arrays, `getSingleType()` needs to skip "null"
Existing IDs to reuse: `request-property-became-nullable`, `request-property-became-not-nullable`, `request-body-became-nullable`, `request-body-became-not-nullable`, `response-property-became-nullable`, `response-body-became-nullable`
Note: The `ListOfTypesDiff` infrastructure and `list_of_types_core.go` checkers already handle oneOf/anyOf type-list patterns. The type array ("null" handling) is different — it's about the `type` field itself, not composition.
1.2 Webhook breaking change rules
Problem: Webhooks are new in 3.1. `WebhooksDiff` already provides `Added`, `Deleted`, and `Modified` (each Modified webhook is a `*PathDiff`, same structure as path diffs). No checker rules reference `WebhooksDiff` today.
Approach: Two layers:
-
Webhook-level rules — new checker file:
- `webhook-added` (INFO)
- `webhook-removed` (ERR — consumers may depend on it)
-
Reuse existing operation rules for webhook operations — Each webhook has operations (POST, etc.) that produce the same `PathDiff` structure as paths. Existing checkers iterate `diffReport.PathsDiff.Modified`. To apply them to webhooks, either:
- (a) Refactor each checker to accept a generic "modified endpoints" map, then call it for both paths and webhooks
- (b) Merge webhook modified operations into PathsDiff before running checkers (with a `webhook:` prefix)
- (c) Duplicate the iteration for webhooks in each checker
Recommended: (b) — merge webhooks into a synthetic paths map with prefixed keys (e.g., `[webhook] newUser`) before calling `CheckBackwardCompatibility`. This gives all 118 existing rules webhook support for free with zero per-checker changes.
New change IDs:
| ID | Level | Message |
|---|---|---|
| `webhook-added` | INFO | `webhook %s added` |
| `webhook-removed` | ERR | `webhook %s removed` |
Priority 2 — Important
2.1 `const` field rules
When a request property's `const` value changes, consumers sending the old value will be rejected. When a response property's `const` changes, consumers expecting the old value will break.
New change IDs:
| ID | Level | Message |
|---|---|---|
| `request-property-const-changed` | ERR | `the request property %s const value changed from %s to %s` |
| `request-property-const-added` | ERR | `the request property %s now requires const value %s` |
| `request-property-const-removed` | INFO | `the request property %s no longer requires a const value` |
| `response-property-const-changed` | ERR | `the response property %s const value changed from %s to %s` |
| `response-property-const-added` | INFO | `the response property %s now has const value %s` |
| `response-property-const-removed` | ERR | `the response property %s no longer has a const value` |
| `request-body-const-changed` | ERR | `the request body const value changed from %s to %s` |
| `response-body-const-changed` | ERR | `the response body const value changed from %s to %s` |
Diff field: `SchemaDiff.ConstDiff` (`*ValueDiff`)
2.2 `exclusiveMinimum` / `exclusiveMaximum` numeric rules
In 3.1 these are numbers, not booleans. The existing `ExclusiveMinDiff` / `ExclusiveMaxDiff` already capture changes via the `ExclusiveBound` union type. The existing min/max checkers may need adjustment to handle the numeric form correctly (currently only check `Min`/`Max`, not `ExclusiveMin`/`ExclusiveMax` as standalone bounds).
Files to modify: `check_breaking_min_max.go` — extend existing min/max comparison logic to also consider exclusive bounds when they are numeric values.
2.3 `prefixItems` rules
Tuple validation. Changes to prefixItems items affect the contract. Uses `SubschemasDiff` (same structure as oneOf/anyOf/allOf).
New change IDs:
| ID | Level | Message |
|---|---|---|
| `request-property-prefix-items-added` | ERR | `added prefix items constraint to request property %s` |
| `request-property-prefix-items-removed` | INFO | `removed prefix items constraint from request property %s` |
| `request-property-prefix-items-updated` | ERR | `prefix items of request property %s changed` |
| `response-property-prefix-items-added` | INFO | `added prefix items constraint to response property %s` |
| `response-property-prefix-items-removed` | ERR | `removed prefix items constraint from response property %s` |
| `response-property-prefix-items-updated` | ERR | `prefix items of response property %s changed` |
Diff field: `SchemaDiff.PrefixItemsDiff` (`*SubschemasDiff`)
2.4 `contains` / `minContains` / `maxContains` rules
Array containment constraints. Adding `contains` to a request is breaking (new constraint). Removing from response is breaking.
New change IDs:
| ID | Level | Message |
|---|---|---|
| `request-property-contains-added` | ERR | `added contains constraint to request property %s` |
| `request-property-contains-removed` | INFO | `removed contains constraint from request property %s` |
| `request-property-contains-updated` | ERR | `contains constraint of request property %s changed` |
| `request-property-min-contains-increased` | ERR | `min contains of request property %s increased` |
| `request-property-min-contains-decreased` | INFO | `min contains of request property %s decreased` |
| `request-property-max-contains-decreased` | ERR | `max contains of request property %s decreased` |
| `request-property-max-contains-increased` | INFO | `max contains of request property %s increased` |
| (mirror for response) |
Diff fields: `SchemaDiff.ContainsDiff`, `MinContainsDiff`, `MaxContainsDiff`
Priority 3 — Nice to have
3.1 `patternProperties` rules
Pattern-based property schemas. Adding a new pattern is tightening for requests, loosening for responses.
Diff field: `SchemaDiff.PatternPropertiesDiff` (`*SchemasDiff` — same structure as `PropertiesDiff`)
3.2 `dependentSchemas` / `dependentRequired` rules
Conditional dependencies. Adding a `dependentRequired` entry on a request property is breaking (new constraint).
Diff fields: `SchemaDiff.DependentSchemasDiff` (`*SchemasDiff`), `DependentRequiredDiff` (`*ValueDiff`)
3.3 `propertyNames` rules
Property name validation. Adding/tightening `propertyNames` on a request is breaking.
Diff field: `SchemaDiff.PropertyNamesDiff` (`*SchemaDiff`)
3.4 `unevaluatedItems` / `unevaluatedProperties` rules
Adding these on a request is a new constraint (breaking). Removing from a response may be breaking.
Diff fields: `SchemaDiff.UnevaluatedItemsDiff`, `UnevaluatedPropertiesDiff` (`*SchemaDiff`)
3.5 `if` / `then` / `else` rules
Conditional schema changes. Complex to analyze breaking-ness — treat any change as a potential breaking change.
Diff fields: `SchemaDiff.IfDiff`, `ThenDiff`, `ElseDiff` (`*SchemaDiff`)
3.6 `contentMediaType` / `contentEncoding` / `contentSchema` rules
Changes to content encoding affect how string values are interpreted.
Diff fields: `SchemaDiff.ContentMediaTypeDiff`, `ContentEncodingDiff` (`*ValueDiff`), `ContentSchemaDiff` (`*SchemaDiff`)
Priority 4 — Informational only
These fields don't affect API contracts and only need INFO-level changelog entries:
| Field | Diff | Note |
|---|---|---|
| `$id` | `SchemaIDDiff` | Identity metadata |
| `$anchor` | `AnchorDiff` | Fragment identifier |
| `$dynamicRef` / `$dynamicAnchor` | `DynamicRefDiff` / `DynamicAnchorDiff` | Dynamic referencing |
| `$schema` | `SchemaDialectDiff` | Dialect declaration |
| `$comment` | `CommentDiff` | Human-readable comment |
| `$defs` | `DefsDiff` | Local schema definitions (only breaking if referenced schemas change, which would be caught by existing property diff rules) |
| `examples` | `ExamplesDiff` | Documentation only |
Implementation order
- 1.2 Webhooks — unblocks all existing rules for webhooks via the merge approach
- 1.1 Nullable type arrays — highest user impact, most requested
- 2.1 const — straightforward ValueDiff checker
- 2.2 exclusiveMin/Max — extend existing min/max checkers
- 2.3-2.4 prefixItems, contains — new SubschemasDiff/SchemaDiff checkers
- P3 items — incremental additions following established patterns
Related
- Parent issue: OpenAPI 3.1 support? #52 (OpenAPI 3.1 support)
- PR feat: add OpenAPI 3.1 support #791 (diff support for OpenAPI 3.1 fields)
- kin-openapi PR feat: add OpenAPI 3.1 support getkin/kin-openapi#1125 (parser/validator support)