Skip to content

Commit 9998528

Browse files
fix: strip __origin__ from slices in example values
stripOriginFromAny only recursed into map[string]any but not []any, so __origin__ keys inside array elements (e.g. example values with lists of objects) survived stripping and caused false positive diffs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6411927 commit 9998528

3 files changed

Lines changed: 85 additions & 7 deletions

File tree

openapi3/origin.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,19 @@ type Location struct {
2626
// dedicated UnmarshalJSON to consume the origin metadata injected by
2727
// the YAML origin-tracking loader.
2828
func stripOriginFromAny(v any) any {
29-
m, ok := v.(map[string]any)
30-
if !ok {
29+
switch x := v.(type) {
30+
case map[string]any:
31+
delete(x, originKey)
32+
for k, val := range x {
33+
x[k] = stripOriginFromAny(val)
34+
}
35+
return x
36+
case []any:
37+
for i, val := range x {
38+
x[i] = stripOriginFromAny(val)
39+
}
40+
return x
41+
default:
3142
return v
3243
}
33-
delete(m, originKey)
34-
for k, val := range m {
35-
m[k] = stripOriginFromAny(val)
36-
}
37-
return m
3844
}

openapi3/origin_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,53 @@ func TestOrigin_XML(t *testing.T) {
436436
base.Origin.Fields["prefix"])
437437
}
438438

439+
func TestStripOriginFromAny_Slice(t *testing.T) {
440+
// Simulates what the YAML origin-tracking loader produces for example
441+
// values that contain arrays of objects (e.g. a list of FASTQ lane pairs).
442+
input := map[string]any{
443+
"name": "test",
444+
"items": []any{
445+
map[string]any{
446+
"__origin__": map[string]any{"file": "a.yaml", "line": 1},
447+
"path": "file1.fastq.gz",
448+
},
449+
map[string]any{
450+
"__origin__": map[string]any{"file": "a.yaml", "line": 2},
451+
"path": "file2.fastq.gz",
452+
},
453+
},
454+
}
455+
456+
result := stripOriginFromAny(input)
457+
m := result.(map[string]any)
458+
items := m["items"].([]any)
459+
for _, item := range items {
460+
itemMap := item.(map[string]any)
461+
require.NotContains(t, itemMap, "__origin__")
462+
}
463+
}
464+
465+
func TestOrigin_ExampleWithArrayValue(t *testing.T) {
466+
loader := NewLoader()
467+
468+
IncludeOrigin = true
469+
defer unsetIncludeOrigin()
470+
471+
doc, err := loader.LoadFromFile("testdata/origin/example_with_array.yaml")
472+
require.NoError(t, err)
473+
474+
example := doc.Paths.Find("/subscribe").Post.RequestBody.Value.Content["application/json"].Examples["bar"]
475+
require.NotNil(t, example.Value)
476+
477+
// The example value contains a list of objects; __origin__ must be stripped from each.
478+
value := example.Value.Value.(map[string]any)
479+
items := value["items"].([]any)
480+
for _, item := range items {
481+
itemMap := item.(map[string]any)
482+
require.NotContains(t, itemMap, "__origin__")
483+
}
484+
}
485+
439486
// TestOrigin_OriginExistsInProperties verifies that loading fails when a specification
440487
// contains a property named "__origin__", highlighting a limitation in the current implementation.
441488
func TestOrigin_OriginExistsInProperties(t *testing.T) {
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
openapi: 3.0.0
2+
info:
3+
title: Example with Array Value
4+
version: 1.0.0
5+
paths:
6+
/subscribe:
7+
post:
8+
requestBody:
9+
content:
10+
application/json:
11+
schema:
12+
type: object
13+
examples:
14+
bar:
15+
summary: A bar example with array
16+
value:
17+
name: test
18+
items:
19+
- path: file1.fastq.gz
20+
size: 1024
21+
- path: file2.fastq.gz
22+
size: 2048
23+
responses:
24+
"200":
25+
description: OK

0 commit comments

Comments
 (0)