OpenApi: drop the storage-type component orphaned by inlining a strong-type wrapper#111
Merged
Merged
Conversation
CoverageLines: 3848 / 5200 (74.0%) Branches: 2061 / 2758 (74.7%) Files changed in this PR
StrongTypes — lines 93.1% (1520/1632), branches 88.2% (997/1130)Booleans — lines 100.0% (14/14), branches 84.6% (22/26)
Collections — lines 90.2% (276/306), branches 85.1% (114/134)
Digits — lines 97.7% (85/87), branches 93.8% (30/32)
Emails — lines 98.6% (68/69), branches 92.5% (37/40)
Enums — lines 100.0% (58/58), branches 95.5% (21/22)
Exceptions — lines 78.9% (15/19), branches 50.0% (7/14)
Maybe — lines 92.1% (151/164), branches 83.9% (94/112)
Nullables — lines 100.0% (12/12), branches 83.3% (20/24)
Numbers — lines 100.0% (84/84), branches 94.4% (17/18)
Result — lines 98.5% (333/338), branches 95.6% (417/436)
Strings — lines 84.7% (133/157), branches 84.4% (54/64)
generated — lines 89.8% (291/324), branches 78.8% (164/208)
StrongTypes.Analyzers — lines 94.7% (322/340), branches 86.8% (132/152)(root) — lines 94.7% (322/340), branches 86.8% (132/152)
StrongTypes.Api — lines 96.7% (327/338), branches 86.3% (88/102)(root) — lines 100.0% (11/11), branches n/a (0/0)
Controllers — lines 96.9% (221/228), branches 86.3% (88/102)
Data — lines 100.0% (54/54), branches n/a (0/0)
Entities — lines 75.0% (12/16), branches n/a (0/0)
Models — lines 100.0% (29/29), branches n/a (0/0)
StrongTypes.AspNetCore — lines 89.3% (151/169), branches 84.5% (71/84)(root) — lines 89.3% (151/169), branches 84.5% (71/84)
StrongTypes.AspNetCore.TestApi — lines 90.7% (39/43), branches 92.5% (37/40)(root) — lines 100.0% (6/6), branches n/a (0/0)
Controllers — lines 89.2% (33/37), branches 92.5% (37/40)
StrongTypes.EfCore — lines 89.3% (117/131), branches 71.4% (40/56)(root) — lines 89.3% (117/131), branches 71.4% (40/56)
StrongTypes.FsCheck — lines 77.4% (96/124), branches n/a (0/0)(root) — lines 77.4% (96/124), branches n/a (0/0)
StrongTypes.OpenApi.Core — lines 91.2% (438/480), branches 83.4% (292/350)(root) — lines 91.8% (256/279), branches 86.3% (164/190)
Inlining — lines 90.5% (182/201), branches 80.0% (128/160)
StrongTypes.OpenApi.Microsoft — lines 39.1% (425/1088), branches 40.3% (171/424)(root) — lines 94.6% (245/259), branches 74.6% (97/130)
Binding — lines 98.1% (103/105), branches 78.6% (55/70)
Collections — lines 100.0% (19/19), branches 100.0% (8/8)
Digits — lines 100.0% (11/11), branches 100.0% (2/2)
Emails — lines 100.0% (11/11), branches 100.0% (2/2)
Inlining — lines 100.0% (7/7), branches 50.0% (1/2)
Maybe — lines 100.0% (12/12), branches 100.0% (2/2)
Numbers — lines 100.0% (8/8), branches 100.0% (2/2)
Strings — lines 100.0% (9/9), branches 100.0% (2/2)
obj/Debug/net10.0/Microsoft.AspNetCore.OpenApi.SourceGenerators/Microsoft.AspNetCore.OpenApi.SourceGenerators.XmlCommentGenerator — lines 0.0% (0/647), branches 0.0% (0/204)
StrongTypes.OpenApi.Swashbuckle — lines 94.1% (239/254), branches 78.3% (166/212)(root) — lines 98.7% (76/77), branches 93.4% (71/76)
Binding — lines 88.3% (106/120), branches 64.3% (72/112)
Collections — lines 100.0% (6/6), branches 100.0% (4/4)
Digits — lines 100.0% (10/10), branches 100.0% (4/4)
Emails — lines 100.0% (10/10), branches 100.0% (4/4)
Inlining — lines 100.0% (2/2), branches n/a (0/0)
Maybe — lines 100.0% (13/13), branches 75.0% (3/4)
Numbers — lines 100.0% (8/8), branches 100.0% (4/4)
Strings — lines 100.0% (8/8), branches 100.0% (4/4)
StrongTypes.OpenApi.TestApi.Microsoft — lines 41.0% (163/398), branches 31.6% (65/206)(root) — lines 100.0% (14/14), branches 100.0% (2/2)
obj/Debug/net10.0/Microsoft.AspNetCore.OpenApi.SourceGenerators/Microsoft.AspNetCore.OpenApi.SourceGenerators.XmlCommentGenerator — lines 38.8% (149/384), branches 30.9% (63/204)
StrongTypes.OpenApi.TestApi.Shared — lines 0.0% (0/192), branches n/a (0/0)(root) — lines 0.0% (0/192), branches n/a (0/0)
StrongTypes.OpenApi.TestApi.Swashbuckle — lines 100.0% (11/11), branches 100.0% (2/2)(root) — lines 100.0% (11/11), branches 100.0% (2/2)
|
cac259e to
cec5d21
Compare
cec5d21 to
da9be3a
Compare
…apper A wrapper whose CLR storage type is an object (Email, backed by System.Net.Mail.MailAddress) makes the generator register that storage type as its own component. Painting the wrapper to its wire primitive severs the only edge to it, so once the wrapper is inlined and dropped the storage type is left as an orphan — openapi-typescript emits it as an unused interface (spurious contract drift the first time Email is used). The inliner already visits every $ref while inlining, so it now records the ones it leaves in place and, afterwards, drops a small known list of generated storage component names (currently just "MailAddress") — but only those still unreferenced. A consumer that uses the type directly (its own DTO with a MailAddress property) keeps it, and a consumer's own schemas are never candidates. Extend the list when a new wrapper drags in another storage type. Living in the shared Core inliner, both the Swashbuckle and Microsoft pipelines benefit. Tests: an integration assertion that the generated document carries no orphaned component schema (reproduced the MailAddress orphan on the Swashbuckle pipeline before the fix), plus a new pipeline-independent StrongTypes.OpenApi.Core.Tests project pinning the scope against the OpenAPI object model — the unreferenced storage type is removed, a still-referenced one is kept, and an unrelated unreferenced consumer schema is preserved. Fixes #110 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
da9be3a to
b765926
Compare
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
Fixes #110. Using the
Emailstrong type on a DTO left an orphanedMailAddresscomponent schema in the generated OpenAPI document. Swashbuckle registersMailAddressas its own component while walkingEmail's CLR properties (Email.Valueis aSystem.Net.Mail.MailAddress); paintingEmaildown to its wire primitive severs the only edge toMailAddress, so onceEmailis inlined and dropped,MailAddressis an orphan with zero inbound$refs.openapi-typescriptthen emits it as an unused interface.Approach
The inliner already visits every
$refwhile inlining, so it now records the ones it leaves in place. Afterwards it drops a small, explicit list of generated storage component names (currently justMailAddress) — but only those still unreferenced:MailAddressproperty) keeps it — the reference check sees it.The production change is ~35 lines in the shared
StrongTypeInliner(StrongTypes.OpenApi.Core), so both the Swashbuckle document filter and the Microsoft document transformer benefit. (Only Swashbuckle exhibited the orphan; the Microsoft pipeline derivesEmail's schema from its JSON contract and never registersMailAddress.)Tests
No_Component_Schema_Is_Left_Unreferenced_After_Inlining, runs on both pipelines) — the generated document carries no orphaned component schema. Failed only on Swashbuckle before the fix, reproducing theMailAddressorphan.StrongTypes.OpenApi.Core.Testsproject exercising the inliner against theMicrosoft.OpenApiobject model directly: the unreferenced storage type is removed, a still-referenced one is kept, and an unrelated unreferenced consumer schema is preserved. The integration suite stays HTTP/JSON-only and no longer references Core.testing.mddocuments the new project.All tests pass (297 integration + 2 Core unit).
Note on the runtime warning
The issue also flagged a
StrongTypeInliningDocumentFilterwarning as "likely the same root cause." It does not reproduce in this repo's test apps (zero occurrences in the test logs), and the reporter attributes it to another OpenAPI transformer in their environment mutating a schema after our adapter runs. The concrete, verifiable defect — the orphanedMailAddress— is what this PR fixes and tests.🤖 Generated with Claude Code