Skip to content

Commit fe47dca

Browse files
openapi3: implement circular reference backtracking (#970)
* feat(loader): implement reference back-tracking * update docs * address review comments * update docs and readme * fix inconsistency * adjust readme
1 parent 57624b3 commit fe47dca

File tree

10 files changed

+149
-152
lines changed

10 files changed

+149
-152
lines changed

.github/docs/openapi3.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,6 @@ var (
5555
// ErrSchemaInputInf may be returned when validating a number
5656
ErrSchemaInputInf = errors.New("floating point Inf is not allowed")
5757
)
58-
var CircularReferenceCounter = 3
59-
var CircularReferenceError = "kin-openapi bug found: circular schema reference not handled"
6058
var DefaultReadFromURI = URIMapCache(ReadFromURIs(ReadFromHTTP(http.DefaultClient), ReadFromFile))
6159
DefaultReadFromURI returns a caching ReadFromURIFunc which can read remote
6260
HTTP URIs and local file URIs.

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,9 @@ for _, path := range doc.Paths.InMatchingOrder() {
303303

304304
## CHANGELOG: Sub-v1 breaking API changes
305305

306+
### v0.126.0
307+
* `openapi3.CircularReferenceError` and `openapi3.CircularReferenceCounter` are removed. `openapi3.Loader` now implements reference backtracking, so any kind of circular references should be properly resolved.
308+
306309
### v0.125.0
307310
* The `openapi3filter.ErrFunc` and `openapi3filter.LogFunc` func types now take the validated request's context as first argument.
308311

cmd/validate/main.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,6 @@ import (
1212
"github.com/getkin/kin-openapi/openapi3"
1313
)
1414

15-
var (
16-
defaultCircular = openapi3.CircularReferenceCounter
17-
circular = flag.Int("circular", defaultCircular, "bump this (upper) limit when there's trouble with cyclic schema references")
18-
)
19-
2015
var (
2116
defaultDefaults = true
2217
defaults = flag.Bool("defaults", defaultDefaults, "when false, disables schemas' default field validation")
@@ -59,7 +54,6 @@ func main() {
5954

6055
switch {
6156
case vd.OpenAPI == "3" || strings.HasPrefix(vd.OpenAPI, "3."):
62-
openapi3.CircularReferenceCounter = *circular
6357
loader := openapi3.NewLoader()
6458
loader.IsExternalRefsAllowed = *ext
6559

@@ -90,9 +84,6 @@ func main() {
9084

9185
case vd.OpenAPI == "2" || strings.HasPrefix(vd.OpenAPI, "2."),
9286
vd.Swagger == "2" || strings.HasPrefix(vd.Swagger, "2."):
93-
if *circular != defaultCircular {
94-
log.Fatal("Flag --circular is only for OpenAPIv3")
95-
}
9687
if *defaults != defaultDefaults {
9788
log.Fatal("Flag --defaults is only for OpenAPIv3")
9889
}

openapi3/issue570_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ import (
99
func TestIssue570(t *testing.T) {
1010
loader := NewLoader()
1111
_, err := loader.LoadFromFile("testdata/issue570.json")
12-
require.ErrorContains(t, err, CircularReferenceError)
12+
require.NoError(t, err)
1313
}

openapi3/issue615_test.go

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,6 @@ import (
99
)
1010

1111
func TestIssue615(t *testing.T) {
12-
{
13-
var old int
14-
old, openapi3.CircularReferenceCounter = openapi3.CircularReferenceCounter, 1
15-
defer func() { openapi3.CircularReferenceCounter = old }()
16-
17-
loader := openapi3.NewLoader()
18-
loader.IsExternalRefsAllowed = true
19-
_, err := loader.LoadFromFile("testdata/recursiveRef/issue615.yml")
20-
require.ErrorContains(t, err, openapi3.CircularReferenceError)
21-
}
22-
23-
var old int
24-
old, openapi3.CircularReferenceCounter = openapi3.CircularReferenceCounter, 4
25-
defer func() { openapi3.CircularReferenceCounter = old }()
26-
2712
loader := openapi3.NewLoader()
2813
loader.IsExternalRefsAllowed = true
2914
doc, err := loader.LoadFromFile("testdata/recursiveRef/issue615.yml")

openapi3/issue796_test.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,6 @@ import (
77
)
88

99
func TestIssue796(t *testing.T) {
10-
var old int
11-
// Need to set CircularReferenceCounter to > 10
12-
old, CircularReferenceCounter = CircularReferenceCounter, 20
13-
defer func() { CircularReferenceCounter = old }()
14-
1510
loader := NewLoader()
1611
doc, err := loader.LoadFromFile("testdata/issue796.yml")
1712
require.NoError(t, err)

openapi3/load_cicular_ref_with_external_file_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,19 @@ func TestLoadCircularRefFromFile(t *testing.T) {
4747
bar.Value.Properties["foo"] = &openapi3.SchemaRef{Ref: "#/components/schemas/Foo", Value: foo.Value}
4848
foo.Value.Properties["bar"] = &openapi3.SchemaRef{Ref: "#/components/schemas/Bar", Value: bar.Value}
4949

50+
bazNestedRef := &openapi3.SchemaRef{Ref: "./baz.yml#/BazNested"}
51+
array := openapi3.NewArraySchema()
52+
array.Items = bazNestedRef
53+
bazNested := &openapi3.Schema{Properties: map[string]*openapi3.SchemaRef{
54+
"bazArray": {
55+
Value: &openapi3.Schema{
56+
Items: bazNestedRef,
57+
},
58+
},
59+
"baz": bazNestedRef,
60+
}}
61+
bazNestedRef.Value = bazNested
62+
5063
want := &openapi3.T{
5164
OpenAPI: "3.0.3",
5265
Info: &openapi3.Info{
@@ -57,6 +70,7 @@ func TestLoadCircularRefFromFile(t *testing.T) {
5770
Schemas: openapi3.Schemas{
5871
"Foo": foo,
5972
"Bar": bar,
73+
"Baz": bazNestedRef,
6074
},
6175
},
6276
}

0 commit comments

Comments
 (0)