Conversation
… conflate (#358) Two documents each defining `#/components/schemas/Foo` used to conflate into one type: the pipeline keys identity on `JsonPointer`, but an external OpenAPI-shaped components library was parsed with a document-relative pointer, so its `Foo` got the same `#/components/schemas/Foo` the root's `Foo` has. Generation didn't fail — it emitted one `foo.dart` for two types (whichever the walk reached first won). Give `JsonPointer` an optional `baseUri` — the document it lives in — that participates in equality but never in `urlEncodedFragment`. Root pointers keep `baseUri: null`; the external components-library parse threads `baseUri: docUri`. So the two `Foo`s become distinct pointers → distinct names (the existing `_1`/`2` disambiguation) → distinct files, while the ref-resolvable fragment (hence the registry key) is unchanged. Single-document output is byte-identical — every tracked fixture regens with zero diff. Split-spec targets already avoided this (they key on the full URI); this brings the components-library branch in line. A discriminator mapping still rebuilds its match pointer from the ref string (`JsonPointer.parse`, base-null), so an external-document discriminator remains a separate, pre-existing limitation.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #364 +/- ##
==========================================
+ Coverage 96.53% 96.69% +0.15%
==========================================
Files 25 25
Lines 5888 5893 +5
==========================================
+ Hits 5684 5698 +14
+ Misses 204 195 -9 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…vention Follow-up to the pointer-identity fix in the same PR. With distinct pointers, the second `Foo` was named by the render-side name allocator's generic fallback (`Foo2`, `foo2.dart`) because the collision resolver only walked the root document and never saw the external schema. Fold external-document schemas (`baseUri != null`) from the assembled registry into `_collectNames`, after the root walk. The two `Foo`s now disambiguate through the same path an in-document collision uses: the root keeps the bare `Foo`, the external one becomes `Foo1` (`foo_1.dart`) — matching `Foo-Bar` + `Foo_Bar` -> `FooBar` + `FooBar1`. Only external schemas are folded in; the registry's copies of the root's own schemas are skipped so they don't double-count into a false self-collision. Byte-identical on every tracked fixture (single-document specs have no external schemas to fold in).
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
Fix multi-document schema-name collision (#358): two documents each
defining
#/components/schemas/Fooused to conflate into one type. Thepipeline keys identity on
JsonPointer, but an external OpenAPI-shapedcomponents library was parsed with a document-relative pointer, so
its
Foogot the same#/components/schemas/Foothe root'sFoohas.Generation didn't fail — it silently emitted one
foo.dartfor twotypes (whichever the walk reached first won).
JsonPointergains an optionalbaseUri— the document it lives in —that participates in equality but never in
urlEncodedFragment.Root pointers keep
baseUri: null; the external components-libraryparse threads
baseUri: docUri. The twoFoos become distinctpointers → distinct types → distinct files, while the ref-resolvable
fragment (hence the registry key a
$refresolves against) is unchanged.The root keeps the bare
Foo; the external one disambiguates toFoo1(
foo_1.dart) — the same_1convention an in-document name collisionalready uses (
Foo-Bar+Foo_Bar→FooBar+FooBar1), rather thanthe name allocator's generic
Foo2fallback.Details
lib/src/types.dart—JsonPointer.fromParts({Uri? baseUri}), carriedthrough
add, added toprops, left out ofurlEncodedFragment.lib/src/parser.dart—ParseContext.baseUrithreads throughMapContext/ListContextchild contexts into thepointergetter.lib/src/assemble.dart— the OpenAPI-shaped components-library parsesets
baseUri: docUri. Split-spec targets already avoided this bykeying on the full URI; this brings the components-library branch in
line.
lib/src/resolver.dart—_collectNamesfolds external-documentschemas from the assembled registry into collision resolution, so a
cross-document collision disambiguates through the same
_1path as anin-document one (root keeps the bare name; only external schemas are
folded in, so the registry's copies of root schemas don't double-count).
Verification
JsonPointeridentity (baseUridisambiguatesequality but not the emitted fragment) and
addpreservation.defining
Foonow emitFoo(localField,foo.dart) andFoo1(
externalField,foo_1.dart) as distinct, analyze-clean types.Confirmed the test fails without the fix and passes with it.
tool/gen_tests.dartregens github/discord/backstage/petstore/spacetraders/train-travel
with zero diff (single-document specs are all
baseUri: nullwith noexternal schemas to fold in).
Known limitation (follow-up)
A discriminator mapping still rebuilds its match pointer from the ref
string (
JsonPointer.parse, base-null), so a discriminator declared inan external document remains a separate, pre-existing limitation
untouched here.