feat(schema): resolve bare $dynamicRef via $dynamicAnchor index#2913
Open
aqeelat wants to merge 1 commit into
Open
feat(schema): resolve bare $dynamicRef via $dynamicAnchor index#2913aqeelat wants to merge 1 commit into
aqeelat wants to merge 1 commit into
Conversation
85e758d to
940c4d0
Compare
…chemaReference Bare $dynamicRef schemas (no $ref) now deserialize as OpenApiSchemaReference whose Target resolves via a per-document $dynamicAnchor index in OpenApiWorkspace. This implements document-scoped resolution per JSON Schema 2020-12 §7.7.2 for the common recursive-type pattern. Changes: - OpenApiWorkspace: add per-document _dynamicAnchorRegistry, populated during RegisterComponents by recursively walking component schemas (including $defs, properties, items, allOf, etc.) - Deserializer (V31/V32): detect bare $dynamicRef via GetDynamicReferencePointer, create OpenApiSchemaReference with IsDynamicRefOnly flag, parse siblings via ApplySchemaMetadata - OpenApiSchemaReference: override Target to resolve via anchor index when IsDynamicRefOnly; return null (no $ref fallback) when anchor is not found or ambiguous - JsonSchemaReference: add IsDynamicRefOnly flag, override SerializeAsV31/V32 to emit siblings without $ref for dynamic-only refs - JsonNodeHelper: add GetDynamicReferencePointer, ExtractDynamicAnchorName, IsFragmentOnlyDynamicRef Siblings ($defs, description, type, etc.) are preserved on the reference via ApplySchemaMetadata and surfaced through the existing Reference-first property getters. Phase 1 of microsoft#2911.
e4c3bd2 to
96e2bcc
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.
Pull Request
Description
Adds support for bare
\$dynamicRef(no\$ref) by resolving it through a workspace-level\$dynamicAnchorindex instead of the\$refURI lookup, and fixes several correctness issues found during review of the initial implementation.Type of Change
Related Issue(s)
None filed.
Changes Made
\$dynamicRefschemas deserialize into anOpenApiSchemaReferencewhoseTargetresolves via a per-document\$dynamicAnchorindex (OpenApiWorkspace). NewJsonSchemaReference.IsDynamicRefOnlydrives serialization that emits\$dynamicRefwithout\$ref.\$dynamicAnchor(previously only top-level + one level of\$defs; anchors underproperties,items,allOf/anyOf/oneOf,if/then/else, etc. were missed). References are treated as leaves so targets aren't followed.\$dynamicAnchorresolution per document. The registry was workspace-global, so two documents in one workspace each declaringnode(the conventional recursive-tree anchor) made resolution returnnullfor both. Now keyed byOpenApiDocument.#node) resolve against the local anchor index; URI-based refs (https://example.com#node) no longer reduce to a bare name and shadow an external target with a local same-named anchor.Targetpath against an empty\$dynamicRefso it returnsnullinstead of falling through tobase.Target(which would resolve the anchor name stored inReference.Idas a component id).type,properties,maxProperties,allOf,\$defs, …) on\$dynamicRefobjects. The bare-ref deserializer path returned beforeParseMap, dropping siblings; now only a pure\$dynamicRefobject becomes a reference, richer objects fall through toParseMap.Testing
New tests in
OpenApiDynamicRefTestscover: bare-ref deserialization, no-\$refserialization, anchor resolution (top-level,\$defs, nestedproperties,allOf), unknown-anchor →null, round-trip through serialization, coexistence with regular\$ref, per-document same-anchor resolution in a shared workspace, absolute-ref non-resolution, and sibling preservation.Microsoft.OpenApi.Readers.Tests: 508 passed.Microsoft.OpenApi.Tests: 1147 passed.Microsoft.OpenApibuilds clean onnet8.0andnetstandard2.0.Checklist
Versions applicability
This targets
main(3.X) per the contributing guidance to start with the uppermost applicable version.Additional Notes
PublicAPI.Unshipped.txt(JsonSchemaReference.SerializeAsV31/V32overrides,OpenApiSchemaReference.Targetoverride).\$dynamicAnchorresolution here is intentionally a single-document, single-candidate match (returnsnullwhen a document declares the same anchor on multiple subschemas — true dynamic scope would require evaluation-path tracking, which is out of scope). Resolving$dynamicRefis also scoped per document per JSON Schema 2020-12, so two documents in a workspace may each declare the same anchor name.$dynamicRefthat cannot be resolved (unknown/ambiguous anchor, or an unloaded external URI) yieldsTarget == null, which makesIOpenApiReferenceHolder.UnresolvedReferencetrue. Because the validator only runs via an explicitValidate()call (not duringLoadAsync), this surfaces only on explicit validation, where flagging an unresolvable$dynamicRefas an unresolved reference is the intended, defensible behavior.referenceIdand therefore lands inReference.Id. It is never used for resolution (Targetre-extracts the anchor from$dynamicRef) or serialization (theIsDynamicRefOnlypath bypasses$ref/ReferenceV3emission), so it is inert. Flagging in case reviewers prefer a dedicated field.OpenApiWorkspace(OpenApiWorkspace)copy constructor was left untouched; it is unused and a proper fix belongs in a separate change.