Repository navigation
fix(spec): keep the stricter constraint when 3.0 conversion folds overlapping keywords - #873
Merged
Merged
Conversation
Preserve inclusive bounds that are tighter and make equal numeric bounds exclusive. Cover inline and component output and the Pydantic producer path. Refs #868 Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
Reuse singleton intersection for null-only schemas and compare JSON values recursively without equating booleans and numbers. Preserve conflicting predicates in allOf. Refs #868 Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
Cap closed prefixes at their position count and retain a tighter maxItems. Include schema tails when detecting homogeneous items or reporting the existing positional-loss approximation. Refs #868 Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
Document stricter numeric bounds, enum/const intersection, closed-prefix caps and positional-loss reporting for differing schema tails. Refs #868 Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Schema conversion still broadens capped arrays with unreachable tail types and incorrectly rejects null for nullable references with generated allOf constraints without a loss warning.
2 open issues
What changed in this PR
This PR tightens OpenAPI 3.0 schema conversion to preserve overlapping JSON Schema constraints, addressing #868. OpenAPI 3.1 and 3.2 output remains unchanged.
Changes:
- Keeps stricter numeric bounds and intersects
enumwithconstusing JSON Schema equality. - Preserves closed-prefix array limits and includes schema tails in positional-loss handling.
- Adds regression coverage and documents the conversion rules.
| File | Description |
|---|---|
tests/test_spec_conversion_characterization.py |
Adds overlap, validation-equivalence, and positional-loss regression cases. |
src/azure_functions_openapi/_schema_version_conversion.py |
Updates bound, enum/const, and prefix/tail conversion logic. |
docs/configuration.md |
Explains overlap handling and positional-loss warnings. |
Review effort: Balanced
OpenAPI 3.0 requires a non-empty enum, so an empty enum next to a const (or a null-only type) is replaced by an allOf member of not: {}, which rejects every instance and is valid in 3.0.
The empty-prefix test no longer pins an items: true output, which OpenAPI 3.0 does not allow; that conversion is unchanged and belongs to a separate fix.
Refs #868
Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
… 3.0 A tail schema beside prefixItems only constrains arrays that can hold more items than the prefix. With a maxItems at or below the prefix length it is unreachable, so appending it widened the positions the prefix does constrain (a string prefix with a number tail accepted [1]) and, being heterogeneous, was reported as a positional loss. The tail now joins the conversion only when maxItems is absent or exceeds the prefix length. Refs #868 Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
Only an enum met by a const or a null-only type is rewritten to not: {}; a standalone empty enum is not normalized.
Refs #868
Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
JSON Schema 2020-12 lets a schema carry keywords that overlap, such as
minimumwithexclusiveMinimum,enumwithconst, andprefixItemswithitems. The OpenAPI 3.0 conversion folded each pair by keeping one side, so the converted schema could accept values the original rejects. This keeps both constraints.Changes
exclusiveMinimum/exclusiveMaximumnext to an inclusive bound: the stricter of the two is kept, and equal bounds stay exclusive. Pydantic emits both forField(ge=5, gt=1); the 3.0 output used to loosen that tominimum: 1with an exclusive flag.constnext toenum: the two are intersected using JSON Schema equality, so a boolean is never equal to1and1.0equals1, including inside lists and objects. Aconstthat is in theenumnarrows it to that value; one that is not leaves a schema that accepts nothing, expressed as an extraallOfmember. An emptyenum, which OpenAPI 3.0 does not allow, becomesnot: {}instead.prefixItemsnext toitems:items: falsenow capsmaxItemsat the prefix length (a tighter existingmaxItemsis kept). Anitemsschema that equals every prefix schema collapses to a plainitems; a different one joins the existingitems.anyOfapproximation and is reported through the same positional-loss warning (an error understrict=True) as a heterogeneous tuple. A tail that amaxItemsat or below the prefix length makes unreachable is ignored.docs/configuration.mddescribes the rules. 3.1 and 3.2 output is unchanged.Validation
make check-allon2f074c2: exit 0, 2116 passed, 30 skipped, coverage 96.40% (gate: 95%). The later commitfd8b6efchanges one documentation sentence.OAS31Validatoron the original,OAS30Validatoron the converted schema), covering positive, negated, property, array-item and combinator positions, inline and as a component: 252,006 comparisons across 4,028 generated documents with no mismatch that is not reported through a warning or a strict-mode error. Re-run on2f074c2.Compatibility
prefixItemsschema whoseitemsdiffers from its prefix and can be reached is now reported with aversion-downgrade-dropwarning; that warning gates--fail-on-warningsand raises understrict=True. Until now the tail was dropped without a message.constoutside itsenum, or an emptyenum) rejectsnull, because the nullable-reference path does not yet reachallOfmembers. Onmainthat input wrongly accepted theconstvalue instead.Related Issues
Closes #868