Skip to content

V10.2.1/normalized semanitc versions - #36

Merged
gimlichael merged 6 commits into
mainfrom
v10.2.1/normalized-semanitc-versions
Jul 9, 2026
Merged

V10.2.1/normalized semanitc versions#36
gimlichael merged 6 commits into
mainfrom
v10.2.1/normalized-semanitc-versions

Conversation

@gimlichael

Copy link
Copy Markdown
Member

This pull request introduces a minor release (v10.2.1) of the Codebelt.Extensions.Asp.Versioning library, focused on improving API version normalization and routing consistency. The main enhancement ensures that semantically equivalent API version formats (such as 1, 1.0, and 1.0.0) are normalized to a canonical form, leading to more robust and predictable version matching. Additional customization options and new tests are included to support and validate this behavior.

Key improvements and fixes:

API Version Normalization and Routing Consistency

  • Added logic to RestfulApiVersionReader to normalize semantically equivalent API version strings to a canonical format, ensuring consistent routing and version matching. This normalization is enabled by default when the default API version is a SemanticApiVersion, but can be opted out of via a compatibility flag (PreviousBehavior). [1] [2]
  • Updated RestfulApiVersioningOptions to include a customizable ApiVersionReader property, allowing advanced customization of how API versions are read from requests. [1] [2]
  • Enhanced AddRestfulApiVersioning to automatically enable version normalization and register semantic version aliases when a SemanticApiVersion is the default.

Documentation and Release Notes

  • Updated documentation and code examples to describe the new normalization behavior, the use of AddApiVersionParser, and the ApiVersionAliasParser for handling short version tokens. [1] [2] [3] [4]
  • Added release notes and changelog entries for v10.2.1, detailing the new features and bug fixes. [1] [2] [3]

Testing Enhancements

  • Added comprehensive functional tests to verify that requests using different but semantically equivalent version tokens are routed correctly, and that mismatched or unregistered aliases are handled with appropriate error responses. [1] [2]

Project Structure

  • Minor documentation update to clarify the location of end-to-end functional tests in the project structure.

aicia-bot and others added 5 commits July 9, 2026 22:55
Add version normalization to RestfulApiVersionReader to treat semantically equivalent versions (1, 1.0, 1.0.0) as identical for routing purposes. Introduces PreviousBehavior compatibility flag to preserve existing behavior when needed. Routes matching on both Accept and Content-Type headers now correctly normalize versions before comparison.
Add comprehensive functional tests for semantic version normalization behavior. Tests verify that Accept and Content-Type header versions with equivalent semantic values (e.g., 1, 1.0, 1.0.0) are correctly normalized and routed to the same endpoint. Includes scenarios for matching versions, mismatched versions, and unregistered version aliases.
Update namespace overview and type documentation for Codebelt.Extensions.Asp.Versioning to reflect version normalization feature. Add new type documentation for ApiVersionAliasParser. Update ServiceCollectionExtensions documentation to include examples and guidance for configuring version normalization behavior.
Update AGENTS.md to reflect the version normalization feature and its behavioral implications for API routing and version matching.
@gimlichael gimlichael self-assigned this Jul 9, 2026
@greptile-apps

greptile-apps Bot commented Jul 9, 2026

Copy link
Copy Markdown

Greptile Summary

This PR introduces v10.2.1 of Codebelt.Extensions.Asp.Versioning, adding normalization of semantically equivalent API version tokens (1, 1.0, 1.0.0) so that requests carrying different alias strings across Accept and Content-Type headers are deduplicated before routing, preventing false 400 ambiguity errors when the default version is a SemanticApiVersion.

  • RestfulApiVersionReader.Read() override: calls base.Read(), then uses the registered IApiVersionParser to canonicalize each version string and deduplicates the result. Normalization is skipped when only one version token is present (Count <= 1) or when PreviousBehavior = true (automatically set for non-SemanticApiVersion defaults).
  • RestfulApiVersioningOptions.ApiVersionReader: new IApiVersionReader escape-hatch property allowing a fully custom reader to replace the default RestfulApiVersionReader.
  • New functional tests: 9-case cross-alias equivalence theory in SemanticApiVersionDefaultCompatibilityTest, plus explicit tests for genuinely-different-version 400 responses and unregistered-alias 415 responses.

Confidence Score: 5/5

Safe to merge; the normalization logic is sound and well-covered by the new functional tests.

The core normalization algorithm correctly handles all observable cases: equivalent aliases deduplicate to a canonical form, genuinely different versions pass through unchanged and are rejected by the framework as ambiguous, and parse failures fall back to original behavior. The PreviousBehavior flag ensures non-semantic defaults are unaffected. Test coverage is thorough across 9 alias-equivalence combinations plus edge cases for different versions and unregistered aliases.

No files require special attention. RestfulApiVersionReader.cs and ServiceCollectionExtensions.cs are the core changed files and both read cleanly.

Important Files Changed

Filename Overview
src/Codebelt.Extensions.Asp.Versioning/RestfulApiVersionReader.cs New Read() override adds normalization and deduplication of semantically-equivalent version tokens from Accept/Content-Type headers; logic is correct but PreviousBehavior is internal-only (already flagged in prior thread)
src/Codebelt.Extensions.Asp.Versioning/ServiceCollectionExtensions.cs Auto-registers alias parser for default SemanticApiVersion and sets PreviousBehavior on reader; double-registration of IApiVersionParser is possible when user also calls AddApiVersionParser
src/Codebelt.Extensions.Asp.Versioning/RestfulApiVersioningOptions.cs Adds IApiVersionReader property for advanced customization; type was widened to IApiVersionReader (previous PR thread covered this)
test/Codebelt.Extensions.Asp.Versioning.FunctionalTests/SemanticApiVersionDefaultCompatibilityTest.cs Adds 9-case cross-alias equivalence theory, a different-versions 400 test, and an unregistered-alias 415 test; good coverage of the normalization behavior
test/Codebelt.Extensions.Asp.Versioning.FunctionalTests/SemanticApiVersionCompatibilityTest.cs Adds test for plain ApiVersion default where different alias tokens across Accept and Content-Type still produce 400; correctly validates PreviousBehavior=true path
CHANGELOG.md Adds v10.2.1 entry with correct links and description of normalization feature

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant C as Client
    participant R as RestfulApiVersionReader
    participant B as MediaTypeApiVersionReader (base)
    participant P as IApiVersionParser
    participant F as Asp.Versioning Framework

    C->>R: "HTTP POST (Accept: v=1, Content-Type: v=1.0)"
    R->>B: base.Read(request)
    B-->>R: ["1", "1.0"]
    Note over R: PreviousBehavior=false AND Count>1
    R->>P: "GetService<IApiVersionParser>()"
    P-->>R: parser (alias-aware)
    R->>P: TryParse("1")
    P-->>R: SemanticApiVersion(1,0,0)
    R->>P: TryParse("1.0")
    P-->>R: SemanticApiVersion(1,0,0)
    Note over R: Deduplicated to ["1.0.0"]
    R-->>F: ["1.0.0"]
    F-->>C: 204 NoContent (routed to v1 endpoint)

    Note over C,F: Non-semantic default (PreviousBehavior=true)
    C->>R: "HTTP POST (Accept: v=1, Content-Type: v=1.0.0)"
    R->>B: base.Read(request)
    B-->>R: ["1", "1.0.0"]
    Note over R: PreviousBehavior=true, early return
    R-->>F: ["1", "1.0.0"]
    F-->>C: 400 BadRequest (ambiguous versions)
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant C as Client
    participant R as RestfulApiVersionReader
    participant B as MediaTypeApiVersionReader (base)
    participant P as IApiVersionParser
    participant F as Asp.Versioning Framework

    C->>R: "HTTP POST (Accept: v=1, Content-Type: v=1.0)"
    R->>B: base.Read(request)
    B-->>R: ["1", "1.0"]
    Note over R: PreviousBehavior=false AND Count>1
    R->>P: "GetService<IApiVersionParser>()"
    P-->>R: parser (alias-aware)
    R->>P: TryParse("1")
    P-->>R: SemanticApiVersion(1,0,0)
    R->>P: TryParse("1.0")
    P-->>R: SemanticApiVersion(1,0,0)
    Note over R: Deduplicated to ["1.0.0"]
    R-->>F: ["1.0.0"]
    F-->>C: 204 NoContent (routed to v1 endpoint)

    Note over C,F: Non-semantic default (PreviousBehavior=true)
    C->>R: "HTTP POST (Accept: v=1, Content-Type: v=1.0.0)"
    R->>B: base.Read(request)
    B-->>R: ["1", "1.0.0"]
    Note over R: PreviousBehavior=true, early return
    R-->>F: ["1", "1.0.0"]
    F-->>C: 400 BadRequest (ambiguous versions)
Loading

Reviews (2): Last reviewed commit: "♻️ update api version reader type and im..." | Re-trigger Greptile

Comment thread src/Codebelt.Extensions.Asp.Versioning/RestfulApiVersioningOptions.cs Outdated
/// <value>The valid accept headers that <see cref="ReadAcceptHeader"/> will filter by.</value>
public IList<string> ValidAcceptHeaders { get; }

internal bool PreviousBehavior { get; set; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 PreviousBehavior is internal but release notes describe it as a user-facing opt-out

The PackageReleaseNotes.txt and CHANGELOG.md both state "a PreviousBehavior compatibility flag available for opting out of the normalization," implying consumers can set it. Because the property has internal visibility, code outside the assembly cannot access it, so there is no public way to disable normalization on a user-constructed RestfulApiVersionReader. Additionally, since PreviousBehavior defaults to false, any RestfulApiVersionReader constructed by a caller and supplied via RestfulApiVersioningOptions.ApiVersionReader will silently have normalization enabled with no escape hatch.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/Codebelt.Extensions.Asp.Versioning/RestfulApiVersionReader.cs
Line: 33

Comment:
`PreviousBehavior` is `internal` but release notes describe it as a user-facing opt-out

The `PackageReleaseNotes.txt` and `CHANGELOG.md` both state "a `PreviousBehavior` compatibility flag available for opting out of the normalization," implying consumers can set it. Because the property has `internal` visibility, code outside the assembly cannot access it, so there is no public way to disable normalization on a user-constructed `RestfulApiVersionReader`. Additionally, since `PreviousBehavior` defaults to `false`, any `RestfulApiVersionReader` constructed by a caller and supplied via `RestfulApiVersioningOptions.ApiVersionReader` will silently have normalization enabled with no escape hatch.

How can I resolve this? If you propose a fix, please make it concise.

@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.23529% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 98.97%. Comparing base (0e51dd3) to head (7a56097).

Files with missing lines Patch % Lines
...tensions.Asp.Versioning/RestfulApiVersionReader.cs 84.00% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #36      +/-   ##
==========================================
- Coverage   99.45%   98.97%   -0.49%     
==========================================
  Files          11       11              
  Lines         553      584      +31     
  Branches       86       93       +7     
==========================================
+ Hits          550      578      +28     
- Misses          3        6       +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@gimlichael
gimlichael merged commit d3aed60 into main Jul 9, 2026
22 of 24 checks passed
@gimlichael
gimlichael deleted the v10.2.1/normalized-semanitc-versions branch July 9, 2026 21:51
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