feat: parse and preserve $dynamicRef (JSON Schema 2020-12)#501
Conversation
…nd-trip Address review feedback on mattpolzin#501: - Add test verifying the dynamic scope travels across a $ref boundary (anchor in Outer's $defs resolved by a $dynamicRef inside a referenced Inner component) -- the key mechanism added by the .reference refactor. - Add test verifying a plain-fragment $ref ("#foo") round-trips verbatim instead of being rewritten with a slash. - Document the $ref plain-fragment round-trip behavior change in the v7 migration guide.
ce0b317 to
73c99fb
Compare
|
Two quick design questions before a deeper review — both could change the shape of this PR, so figured I'd surface them up front. 1. Target branch. This adds new cases to public enums ( 2. AST shape. I went with a dedicated The rest (parse / decode / encode, the decoder no longer warning on |
|
Hey, thanks for the PR. Just a heads up that I am slow to review non-critical PRs lately for a few reasons but especially given I am on vacation without my computer for another week. Not ignoring you, though! |
|
No worries. Enjoy your vacation. Should I ping you again in two weeks? |
|
Hi @mattpolzin |
mattpolzin
left a comment
There was a problem hiding this comment.
Thanks for reminding me to get this reviewed. It's a busy time of life but I do appreciate the contribution.
Can you point this PR at the release/7_0 branch since it contains breaking changes? The updates you are making to the migration guide here look nice.
I'll want to spend a few minutes poking at the code in the next few days here but for now I just have the one high level question I left inline about whether it is justified to retain dynamic references in Dereferenced... types vs. requiring them all to be able to be derefenced.
73c99fb to
2fa36f2
Compare
Add parsing and round-trip serialization for the JSON Schema 2020-12 $dynamicRef / $dynamicAnchor keywords (spec §7.7), adopted by OpenAPI 3.1.x as its schema dialect. $dynamicAnchor parsing landed in mattpolzin#360, but $dynamicRef was previously dropped: schemas whose only attribute was $dynamicRef decoded as empty fragments with a 'Found nothing but unsupported attributes' warning. With this change the keyword is parsed and preserved on the JSONSchema AST, unblocking downstream tooling (e.g. apple/swift-openapi-generator#547) to observe it. Scope (parse/preserve only): - New JSONDynamicReference type wrapping JSONReference<JSONSchema> with $dynamicRef encode/decode. - New .anchor(String) case on JSONReference.InternalReference so plain fragment references like '#category' parse and round-trip. - New .dynamicReference case on JSONSchema.Schema, threaded through every exhaustive switch and the schema transformations (factory, encode/decode, optional/required/nullable, with(...), fragment combining, external dereferencing). - Decoder recognizes $dynamicRef before the unsupported-attributes fallthrough. - DereferencedJSONSchema retains no reference cases: local dereferencing throws on .dynamicReference (dynamic-scope resolution is a follow-up, tracked in mattpolzin#359). The raw JSONSchema AST still carries .dynamicReference for tools that read schemas without dereferencing. This is a source-breaking change (new public enum cases). The v7 migration guide is added documenting it. Part of mattpolzin#359.
2fa36f2 to
506c075
Compare
|
Thanks for the review, @mattpolzin. All three points addressed:
Also squashed into a single commit and cleaned up a few minor things (doc comments, As a sanity check, I validated the AST against |
mattpolzin
left a comment
There was a problem hiding this comment.
Alright, I've given this a more thorough review now that I've had time to sit down with the code open in an editor and I just have a few small remaining requests before merging.
Thanks for your patience and effort!
Addresses review feedback on mattpolzin#501: - Migration guide: only JSONSchema.Schema gains .dynamicReference (DereferencedJSONSchema no longer has the case); drop the paragraph describing Swift switch exhaustiveness mechanics. - DereferencedJSONSchema: add a TODO on the external-deref dynamicReference case noting external dereferencing is deferred (see mattpolzin#359). Part of mattpolzin#359.
|
Thank you for taking the time. |
|
Thanks! |
Part of #359.
Summary
Adds parsing and round-trip serialization for the JSON Schema 2020-12
$dynamicRef/$dynamicAnchorkeywords (spec §7.7), which OpenAPI 3.1.x adopts as its schema dialect.$dynamicAnchorparsing landed in #360, but$dynamicRefwas dropped: schemas whose only attribute was$dynamicRefdecoded as empty fragments with a "Found nothing but unsupported attributes" warning. This PR makes the keyword parse and round-trip on theJSONSchemaAST.Changes
JSONDynamicReferencetype wrappingJSONReference<JSONSchema>with$dynamicRefencode/decode..anchor(String)case onJSONReference.InternalReferenceso plain fragment references like#categoryparse and round-trip..dynamicReferencecase onJSONSchema.Schema, threaded through every exhaustive switch and the schema transformations (factory, encode/decode, optional/required/nullable,with(...), fragment combining, external dereferencing).$dynamicRefbefore the unsupported-attributes fallthrough.DereferencedJSONSchemaretains no reference cases: per review feedback, local dereferencing throws when it encounters a.dynamicReferenceit cannot inline (dynamic-scope resolution is a follow-up), preserving theDereferenced...invariant that no references remain. The rawJSONSchemaAST still carries.dynamicReferencefor tools that read schemas without dereferencing.Behavior
$dynamicRef-only schema → decodes as.dynamicReference, no warning (previously an empty.fragment+ "unsupported attributes").$dynamicRefsurvives encode/decode round-trips, including inside real document trees.dereferenced(in:)/locallyDereferenced()throws on a$dynamicRefuntil the resolution follow-up lands (consistent with how unresolvable static$refvalues fail).Testing
JSONSchemaDynamicReferenceTests: decode (anchor/component), the no-"unsupported-attributes" regression, encode round-trip, a full document round-trip, accessors/transformations,$refplain-fragment round-trip, and athrows-on-dereferencing test.recursive-category-tree.yaml—$dynamicRefnow survives decode (previously stripped).Breaking change
Source-breaking (new public enum cases), so targets
release/7_0. The v7 migration guide is added documenting it.This PR was drafted with assistance from AI tooling. The submitter has reviewed and validated the contents prior to submission.