Skip to content

Commit

Permalink
fixes #16 #17
Browse files Browse the repository at this point in the history
  • Loading branch information
chanced authored Sep 28, 2022
1 parent 0205417 commit d1a9f0c
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 22 deletions.
53 changes: 39 additions & 14 deletions load.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ type loader struct {
nodes map[string]nodectx
dynamicRefs []refctx
refs []refctx
dialect *uri.URI
}

func (l *loader) load(ctx context.Context, location uri.URI, ek Kind, openapi *semver.Version, dialect *uri.URI) (Node, error) {
Expand All @@ -144,6 +145,10 @@ func (l *loader) load(ctx context.Context, location uri.URI, ek Kind, openapi *s
if err != nil {
return nil, err
}
if openapi == nil && l.doc != nil {
openapi = l.doc.OpenAPI
}

switch k {
case KindDocument:
return l.loadDocument(ctx, data, location)
Expand Down Expand Up @@ -196,6 +201,8 @@ func (l *loader) loadDocument(ctx context.Context, data []byte, u uri.URI) (*Doc
if err != nil {
return nil, NewError(fmt.Errorf("failed to determine OpenAPI schema dialect: %w", err), u)
}
l.dialect = sd

if sd == nil {
return nil, NewError(fmt.Errorf("failed to determine OpenAPI schema dialect"), u)
}
Expand Down Expand Up @@ -229,7 +236,7 @@ func (l *loader) loadDocument(ctx context.Context, data []byte, u uri.URI) (*Doc
dc.anchors = anchors

l.nodes[u.String()] = dc
if err = l.init(&dc, &dc, doc.nodes(), *v, *sd); err != nil {
if err = l.traverse(&dc, &dc, doc.nodes(), *v, *sd); err != nil {
return nil, err
}
// we only traverse the references after the top-level document is fully
Expand Down Expand Up @@ -258,7 +265,7 @@ func (l *loader) loadDocument(ctx context.Context, data []byte, u uri.URI) (*Doc
r.root.resolvedRefs = append(r.root.resolvedRefs, r)
}
for _, n := range nodes {
if err = l.init(&dc, n.root, n.nodes(), n.openapi, n.jsonschema); err != nil {
if err = l.traverse(&dc, n.root, n.nodes(), n.openapi, n.jsonschema); err != nil {
return nil, err
}
}
Expand Down Expand Up @@ -315,17 +322,25 @@ func (l *loader) resolveRemoteRef(ctx context.Context, r refctx) (*nodectx, erro
return nil, NewError(fmt.Errorf("openapi: ref URI not found: %s", u), r.AbsoluteLocation())
}
} else {
// we need to load the root resource first we need to load the resource
// we need to check to see if there is a reference pointing to the root first
// so we know what the expected type is
rus := rooturi.String()
for _, x := range l.refs {
if x.URI().String() == rus {
// found it. we load that one first.
if _, err := l.load(ctx, rooturi, x.RefKind(), nil, nil); err != nil {
return nil, err

// if this is ref points to the root of a file, we need to load it
if u.String() == rus {
// the ref is the root so we need to load it
if _, err := l.load(ctx, *u, r.RefKind(), nil, nil); err != nil {
return nil, err
}
} else {
// otherwise we need to check to see if there is a ref pointing to
// the root so we know what the expected kind is
for _, x := range l.refs {
if x.URI().String() == rus {
// found it. we load that one first.
if _, err := l.load(ctx, rooturi, x.RefKind(), nil, nil); err != nil {
return nil, err
}
break
}
break
}
}

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

func (l *loader) init(node *nodectx, root *nodectx, nodes []node, openapi semver.Version, jsonschema uri.URI) error {
func (l *loader) traverse(node *nodectx, root *nodectx, nodes []node, openapi semver.Version, jsonschema uri.URI) error {
for _, n := range nodes {
nc, err := newNodeCtx(n, root, &openapi, &jsonschema)
if err != nil {
Expand All @@ -518,9 +533,9 @@ func (l *loader) init(node *nodectx, root *nodectx, nodes []node, openapi semver
if !r.IsResolved() {
l.refs = append(l.refs, refctx{root: root, in: node, ref: r, openapi: nc.openapi, jsonschema: nc.jsonschema})
}
return nil
continue
}
if err := l.init(&nc, root, n.nodes(), nc.openapi, nc.jsonschema); err != nil {
if err := l.traverse(&nc, root, n.nodes(), nc.openapi, nc.jsonschema); err != nil {
return err
}
}
Expand All @@ -539,6 +554,7 @@ func (l *loader) loadSchema(ctx context.Context, data []byte, u uri.URI, v semve
s.setLocation(loc)
nc := nodectx{node: &s, openapi: v, jsonschema: u}
nc.root = &nc

a, err := s.Anchors()
if err != nil {
return nil, fmt.Errorf("failed to load anchors: %w", err)
Expand All @@ -555,6 +571,15 @@ func (l *loader) loadSchema(ctx context.Context, data []byte, u uri.URI, v semve
} else {
l.nodes[u.String()] = nc
}

d := s.Schema
if d == nil {
d = l.dialect
}
if err = l.traverse(&nc, &nc, s.nodes(), *l.doc.OpenAPI, *d); err != nil {
return nil, err
}

return &s, nil
}

Expand Down
59 changes: 51 additions & 8 deletions load_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"embed"
"fmt"
"io"
"io/fs"
"testing"

"github.com/Masterminds/semver"
Expand All @@ -22,13 +23,14 @@ func TestLoadRefComponent(t *testing.T) {
t.Fatal(err)
}
ctx := context.Background()
doc, err := openapi.Load(ctx, "testdata/documents/comprefs.yaml", NoopValidator{}, func(ctx context.Context, uri uri.URI, kind openapi.Kind) (openapi.Kind, []byte, error) {
loadfn := func(ctx context.Context, uri uri.URI, kind openapi.Kind) (openapi.Kind, []byte, error) {
b, err := io.ReadAll(f)
if err != nil {
return 0, nil, err
}
return openapi.KindDocument, b, nil
})
}
doc, err := openapi.Load(ctx, "testdata/documents/comprefs.yaml", NoopValidator{}, loadfn)
if err != nil {
t.Error(err)
}
Expand All @@ -53,27 +55,68 @@ func TestLoadRefComponent(t *testing.T) {
}
}

func TestLoad(t *testing.T) {
f, err := testdata.Open("testdata/documents/petstore.yaml")
func TestLoad_Local(t *testing.T) {
ctx := context.Background()
dir, err := fs.Sub(testdata, "testdata")
if err != nil {
t.Fatal(err)
}
ctx := context.Background()
doc, err := openapi.Load(ctx, "testdata/documents/petstore.yaml", NoopValidator{}, func(ctx context.Context, uri uri.URI, kind openapi.Kind) (openapi.Kind, []byte, error) {
f, err := dir.Open("documents/petstore.yaml")
if err != nil {
t.Fatal(err)
}

loadfn := func(ctx context.Context, uri uri.URI, kind openapi.Kind) (openapi.Kind, []byte, error) {
b, err := io.ReadAll(f)
// fmt.Println(string(b))
if err != nil {
return 0, nil, err
}
return openapi.KindDocument, b, nil
})
}

doc, err := openapi.Load(ctx, "https://documents/petstore.yaml", NoopValidator{}, loadfn)
if err != nil {
t.Error(err)
}
if doc == nil {
t.Errorf("failed to load document")
}
}

func TestLoad_Remote(t *testing.T) {
ctx := context.Background()
dir, err := fs.Sub(testdata, "testdata")
if err != nil {
t.Fatal(err)
}
loadfn := func(ctx context.Context, uri uri.URI, kind openapi.Kind) (openapi.Kind, []byte, error) {
var f fs.File
var err error
if uri.String() == "https://example.com/schemas/string-map" {
f, err = dir.Open("schemas/string-map.yaml")
} else {
f, err = dir.Open("documents/remote-refs.yaml")
}
if err != nil {
return 0, nil, err
}
b, err := io.ReadAll(f)
if err != nil {
return 0, nil, err
}
fmt.Println("kind", kind)
return kind, b, nil
}

// testing loading remote refs
doc, err := openapi.Load(ctx, "testdata/documents/remote-refs.yaml", NoopValidator{}, loadfn)
if err != nil {
t.Error(err)
}
if doc == nil {
t.Errorf("failed to load document")
}
// litter.Dump(doc)
}

func TestDynamicRefs(t *testing.T) {
Expand Down
13 changes: 13 additions & 0 deletions testdata/documents/remote-refs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
openapi: 3.1.0
paths:
/generic:
# parameters:
# - name: objparam
# style:
post:
operationId: createGenericMap
requestBody:
content:
application/json:
schema:
$ref: https://example.com/schemas/string-map
8 changes: 8 additions & 0 deletions testdata/schemas/string-map.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
$schema: https://json-schema.org/draft/2020-12/schema
$id: https://example.com/schemas/string-map
$ref: "#/$defs/StringMap"
$defs:
StringMap:
type: object
additionalProperties:
type: string

0 comments on commit d1a9f0c

Please sign in to comment.