Skip to content

Commit d1a9f0c

Browse files
authored
fixes #16 #17
1 parent 0205417 commit d1a9f0c

File tree

4 files changed

+111
-22
lines changed

4 files changed

+111
-22
lines changed

load.go

+39-14
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ type loader struct {
134134
nodes map[string]nodectx
135135
dynamicRefs []refctx
136136
refs []refctx
137+
dialect *uri.URI
137138
}
138139

139140
func (l *loader) load(ctx context.Context, location uri.URI, ek Kind, openapi *semver.Version, dialect *uri.URI) (Node, error) {
@@ -144,6 +145,10 @@ func (l *loader) load(ctx context.Context, location uri.URI, ek Kind, openapi *s
144145
if err != nil {
145146
return nil, err
146147
}
148+
if openapi == nil && l.doc != nil {
149+
openapi = l.doc.OpenAPI
150+
}
151+
147152
switch k {
148153
case KindDocument:
149154
return l.loadDocument(ctx, data, location)
@@ -196,6 +201,8 @@ func (l *loader) loadDocument(ctx context.Context, data []byte, u uri.URI) (*Doc
196201
if err != nil {
197202
return nil, NewError(fmt.Errorf("failed to determine OpenAPI schema dialect: %w", err), u)
198203
}
204+
l.dialect = sd
205+
199206
if sd == nil {
200207
return nil, NewError(fmt.Errorf("failed to determine OpenAPI schema dialect"), u)
201208
}
@@ -229,7 +236,7 @@ func (l *loader) loadDocument(ctx context.Context, data []byte, u uri.URI) (*Doc
229236
dc.anchors = anchors
230237

231238
l.nodes[u.String()] = dc
232-
if err = l.init(&dc, &dc, doc.nodes(), *v, *sd); err != nil {
239+
if err = l.traverse(&dc, &dc, doc.nodes(), *v, *sd); err != nil {
233240
return nil, err
234241
}
235242
// we only traverse the references after the top-level document is fully
@@ -258,7 +265,7 @@ func (l *loader) loadDocument(ctx context.Context, data []byte, u uri.URI) (*Doc
258265
r.root.resolvedRefs = append(r.root.resolvedRefs, r)
259266
}
260267
for _, n := range nodes {
261-
if err = l.init(&dc, n.root, n.nodes(), n.openapi, n.jsonschema); err != nil {
268+
if err = l.traverse(&dc, n.root, n.nodes(), n.openapi, n.jsonschema); err != nil {
262269
return nil, err
263270
}
264271
}
@@ -315,17 +322,25 @@ func (l *loader) resolveRemoteRef(ctx context.Context, r refctx) (*nodectx, erro
315322
return nil, NewError(fmt.Errorf("openapi: ref URI not found: %s", u), r.AbsoluteLocation())
316323
}
317324
} else {
318-
// we need to load the root resource first we need to load the resource
319-
// we need to check to see if there is a reference pointing to the root first
320-
// so we know what the expected type is
321325
rus := rooturi.String()
322-
for _, x := range l.refs {
323-
if x.URI().String() == rus {
324-
// found it. we load that one first.
325-
if _, err := l.load(ctx, rooturi, x.RefKind(), nil, nil); err != nil {
326-
return nil, err
326+
327+
// if this is ref points to the root of a file, we need to load it
328+
if u.String() == rus {
329+
// the ref is the root so we need to load it
330+
if _, err := l.load(ctx, *u, r.RefKind(), nil, nil); err != nil {
331+
return nil, err
332+
}
333+
} else {
334+
// otherwise we need to check to see if there is a ref pointing to
335+
// the root so we know what the expected kind is
336+
for _, x := range l.refs {
337+
if x.URI().String() == rus {
338+
// found it. we load that one first.
339+
if _, err := l.load(ctx, rooturi, x.RefKind(), nil, nil); err != nil {
340+
return nil, err
341+
}
342+
break
327343
}
328-
break
329344
}
330345
}
331346

@@ -504,7 +519,7 @@ func (l *loader) getDocumentSchemaDialect(doc *Document) (*uri.URI, error) {
504519
return nil, fmt.Errorf("failed to determine OpenAPI schema dialect")
505520
}
506521

507-
func (l *loader) init(node *nodectx, root *nodectx, nodes []node, openapi semver.Version, jsonschema uri.URI) error {
522+
func (l *loader) traverse(node *nodectx, root *nodectx, nodes []node, openapi semver.Version, jsonschema uri.URI) error {
508523
for _, n := range nodes {
509524
nc, err := newNodeCtx(n, root, &openapi, &jsonschema)
510525
if err != nil {
@@ -518,9 +533,9 @@ func (l *loader) init(node *nodectx, root *nodectx, nodes []node, openapi semver
518533
if !r.IsResolved() {
519534
l.refs = append(l.refs, refctx{root: root, in: node, ref: r, openapi: nc.openapi, jsonschema: nc.jsonschema})
520535
}
521-
return nil
536+
continue
522537
}
523-
if err := l.init(&nc, root, n.nodes(), nc.openapi, nc.jsonschema); err != nil {
538+
if err := l.traverse(&nc, root, n.nodes(), nc.openapi, nc.jsonschema); err != nil {
524539
return err
525540
}
526541
}
@@ -539,6 +554,7 @@ func (l *loader) loadSchema(ctx context.Context, data []byte, u uri.URI, v semve
539554
s.setLocation(loc)
540555
nc := nodectx{node: &s, openapi: v, jsonschema: u}
541556
nc.root = &nc
557+
542558
a, err := s.Anchors()
543559
if err != nil {
544560
return nil, fmt.Errorf("failed to load anchors: %w", err)
@@ -555,6 +571,15 @@ func (l *loader) loadSchema(ctx context.Context, data []byte, u uri.URI, v semve
555571
} else {
556572
l.nodes[u.String()] = nc
557573
}
574+
575+
d := s.Schema
576+
if d == nil {
577+
d = l.dialect
578+
}
579+
if err = l.traverse(&nc, &nc, s.nodes(), *l.doc.OpenAPI, *d); err != nil {
580+
return nil, err
581+
}
582+
558583
return &s, nil
559584
}
560585

load_test.go

+51-8
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"embed"
66
"fmt"
77
"io"
8+
"io/fs"
89
"testing"
910

1011
"github.com/Masterminds/semver"
@@ -22,13 +23,14 @@ func TestLoadRefComponent(t *testing.T) {
2223
t.Fatal(err)
2324
}
2425
ctx := context.Background()
25-
doc, err := openapi.Load(ctx, "testdata/documents/comprefs.yaml", NoopValidator{}, func(ctx context.Context, uri uri.URI, kind openapi.Kind) (openapi.Kind, []byte, error) {
26+
loadfn := func(ctx context.Context, uri uri.URI, kind openapi.Kind) (openapi.Kind, []byte, error) {
2627
b, err := io.ReadAll(f)
2728
if err != nil {
2829
return 0, nil, err
2930
}
3031
return openapi.KindDocument, b, nil
31-
})
32+
}
33+
doc, err := openapi.Load(ctx, "testdata/documents/comprefs.yaml", NoopValidator{}, loadfn)
3234
if err != nil {
3335
t.Error(err)
3436
}
@@ -53,27 +55,68 @@ func TestLoadRefComponent(t *testing.T) {
5355
}
5456
}
5557

56-
func TestLoad(t *testing.T) {
57-
f, err := testdata.Open("testdata/documents/petstore.yaml")
58+
func TestLoad_Local(t *testing.T) {
59+
ctx := context.Background()
60+
dir, err := fs.Sub(testdata, "testdata")
5861
if err != nil {
5962
t.Fatal(err)
6063
}
61-
ctx := context.Background()
62-
doc, err := openapi.Load(ctx, "testdata/documents/petstore.yaml", NoopValidator{}, func(ctx context.Context, uri uri.URI, kind openapi.Kind) (openapi.Kind, []byte, error) {
64+
f, err := dir.Open("documents/petstore.yaml")
65+
if err != nil {
66+
t.Fatal(err)
67+
}
68+
69+
loadfn := func(ctx context.Context, uri uri.URI, kind openapi.Kind) (openapi.Kind, []byte, error) {
6370
b, err := io.ReadAll(f)
6471
// fmt.Println(string(b))
6572
if err != nil {
6673
return 0, nil, err
6774
}
6875
return openapi.KindDocument, b, nil
69-
})
76+
}
77+
78+
doc, err := openapi.Load(ctx, "https://documents/petstore.yaml", NoopValidator{}, loadfn)
79+
if err != nil {
80+
t.Error(err)
81+
}
82+
if doc == nil {
83+
t.Errorf("failed to load document")
84+
}
85+
}
86+
87+
func TestLoad_Remote(t *testing.T) {
88+
ctx := context.Background()
89+
dir, err := fs.Sub(testdata, "testdata")
90+
if err != nil {
91+
t.Fatal(err)
92+
}
93+
loadfn := func(ctx context.Context, uri uri.URI, kind openapi.Kind) (openapi.Kind, []byte, error) {
94+
var f fs.File
95+
var err error
96+
if uri.String() == "https://example.com/schemas/string-map" {
97+
f, err = dir.Open("schemas/string-map.yaml")
98+
} else {
99+
f, err = dir.Open("documents/remote-refs.yaml")
100+
}
101+
if err != nil {
102+
return 0, nil, err
103+
}
104+
b, err := io.ReadAll(f)
105+
if err != nil {
106+
return 0, nil, err
107+
}
108+
fmt.Println("kind", kind)
109+
return kind, b, nil
110+
}
111+
112+
// testing loading remote refs
113+
doc, err := openapi.Load(ctx, "testdata/documents/remote-refs.yaml", NoopValidator{}, loadfn)
70114
if err != nil {
71115
t.Error(err)
72116
}
73117
if doc == nil {
74118
t.Errorf("failed to load document")
75119
}
76-
// litter.Dump(doc)
77120
}
78121

79122
func TestDynamicRefs(t *testing.T) {

testdata/documents/remote-refs.yaml

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
openapi: 3.1.0
2+
paths:
3+
/generic:
4+
# parameters:
5+
# - name: objparam
6+
# style:
7+
post:
8+
operationId: createGenericMap
9+
requestBody:
10+
content:
11+
application/json:
12+
schema:
13+
$ref: https://example.com/schemas/string-map

testdata/schemas/string-map.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
$schema: https://json-schema.org/draft/2020-12/schema
2+
$id: https://example.com/schemas/string-map
3+
$ref: "#/$defs/StringMap"
4+
$defs:
5+
StringMap:
6+
type: object
7+
additionalProperties:
8+
type: string

0 commit comments

Comments
 (0)