Skip to content

Commit 903dfce

Browse files
fix: fixed issues with navigating with jsonpointers through unknown properties
1 parent 885ea5c commit 903dfce

File tree

4 files changed

+68
-5
lines changed

4 files changed

+68
-5
lines changed

jsonpointer/jsonpointer.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,23 @@ func getKeyBasedStructTarget(sourceVal reflect.Value, currentPart navigationPart
313313
}
314314
}
315315

316+
// Field not found in struct fields, try searching the associated YAML node if available
317+
// Check if the struct implements RootNodeAccessor interface (which has GetRootNode)
318+
type rootNodeAccessor interface {
319+
GetRootNode() *yaml.Node
320+
}
321+
322+
if rootNodeAccessor, ok := sourceVal.Interface().(rootNodeAccessor); ok {
323+
rootNode := rootNodeAccessor.GetRootNode()
324+
if rootNode != nil {
325+
// Use the existing YAML node navigation logic to search for the key
326+
result, newStack, err := getYamlNodeTarget(rootNode, currentPart, stack, currentPath, o)
327+
if err == nil {
328+
return result, newStack, nil
329+
}
330+
}
331+
}
332+
316333
return nil, nil, ErrNotFound.Wrap(fmt.Errorf("key %s not found in %v at %s", key, sourceVal.Type(), currentPath))
317334
}
318335

jsonpointer/models.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@ func navigateModel(sourceVal reflect.Value, currentPart navigationPart, stack []
107107

108108
if coreFieldIndex == -1 {
109109
// Field not found in core model, try searching the associated YAML node
110-
// Check if the model implements CoreModeler interface (which has GetRootNode)
111-
if coreModeler, ok := coreAny.(marshaller.CoreModeler); ok {
112-
rootNode := coreModeler.GetRootNode()
110+
// Check if the high-level model implements RootNodeAccessor interface (which has GetRootNode)
111+
if rootNodeAccessor, ok := sourceVal.Interface().(marshaller.RootNodeAccessor); ok {
112+
rootNode := rootNodeAccessor.GetRootNode()
113113
if rootNode != nil {
114114
// Use the existing YAML node navigation logic to search for the key
115115
result, newStack, err := getYamlNodeTarget(rootNode, currentPart, stack, currentPath, o)

jsonpointer/models_yaml_fallback_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,50 @@ func getTypeName(v interface{}) string {
195195
}
196196
return reflect.TypeOf(v).String()
197197
}
198+
199+
// TestNavigateModel_YAMLFallback_KeyBasedStruct tests we can access the RootNode from Key based structs like our core models
200+
// This validates the fix where GetTarget can navigate from a core model to additional YAML properties
201+
func TestNavigateModel_YAMLFallback_KeyBasedStruct(t *testing.T) {
202+
t.Parallel()
203+
204+
yamlContent := `
205+
knownField: "test"
206+
definitions:
207+
attachments:
208+
title: Attachments
209+
description: "Array of attachments"
210+
type: array
211+
items:
212+
type: object
213+
`
214+
215+
// Parse YAML content
216+
var rootNode yaml.Node
217+
err := yaml.Unmarshal([]byte(yamlContent), &rootNode)
218+
require.NoError(t, err, "failed to parse YAML")
219+
220+
// Create test model with YAML node
221+
model := &TestModel{}
222+
model.GetCore().SetRootNode(&rootNode)
223+
224+
// Test navigation from the CORE MODEL (this was the bug - GetTarget couldn't find YAML properties from core model)
225+
coreModel := model.GetCore()
226+
227+
// Test navigation to definitions key (not in core model)
228+
result, err := GetTarget(coreModel, "/definitions")
229+
require.NoError(t, err, "should find definitions in YAML from core model")
230+
require.NotNil(t, result, "result should not be nil")
231+
232+
yamlNode, ok := result.(*yaml.Node)
233+
require.True(t, ok, "should return yaml.Node for definitions")
234+
assert.Equal(t, yaml.MappingNode, yamlNode.Kind, "definitions should be a mapping node")
235+
236+
// Test navigation deeper into definitions structure from core model
237+
result, err = GetTarget(coreModel, "/definitions/attachments/title")
238+
require.NoError(t, err, "should navigate through YAML structure from core model")
239+
require.NotNil(t, result, "result should not be nil")
240+
241+
yamlNode, ok = result.(*yaml.Node)
242+
require.True(t, ok, "should return yaml.Node")
243+
assert.Equal(t, "Attachments", yamlNode.Value, "title value should match")
244+
}

jsonschema/oas3/validation.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,7 @@ func getRootCauses(err *jsValidator.ValidationError, js core.Schema) []error {
145145

146146
t, err := jsonpointer.GetTarget(js, errJP, jsonpointer.WithStructTags("key"))
147147
if err != nil {
148-
// TODO need to potentially handle this in another way
149-
errs = append(errs, err)
148+
errs = append(errs, validation.NewValidationError(err, js.GetRootNode()))
150149
continue
151150
}
152151

0 commit comments

Comments
 (0)