Skip to content

Commit

Permalink
added remote schema cache. Loaded schemas are stored in the cache onc…
Browse files Browse the repository at this point in the history
…e they are loaded and resolved.

this avoids hundreds of roundtrips to remote server for schemas that contain multiple references
  • Loading branch information
bonnyr-f5 committed Sep 24, 2019
1 parent 8785b41 commit bd0b49a
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 5 deletions.
11 changes: 8 additions & 3 deletions openapi3/swagger_loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ type SwaggerLoader struct {
Context context.Context
LoadSwaggerFromURIFunc func(loader *SwaggerLoader, url *url.URL) (*Swagger, error)
visited map[interface{}]struct{}
loadedRemoteSchemas map[url.URL]*Swagger
}

func NewSwaggerLoader() *SwaggerLoader {
return &SwaggerLoader{}
return &SwaggerLoader{loadedRemoteSchemas: map[url.URL]*Swagger{}}
}

func (swaggerLoader *SwaggerLoader) LoadSwaggerFromURI(location *url.URL) (*Swagger, error) {
Expand Down Expand Up @@ -222,9 +223,13 @@ func (swaggerLoader *SwaggerLoader) resolveComponent(swagger *Swagger, ref strin
return nil, "", nil, fmt.Errorf("Error while resolving path: %v", err)
}

if swagger, err = swaggerLoader.LoadSwaggerFromURI(resolvedPath); err != nil {
return nil, "", nil, fmt.Errorf("Error while resolving reference '%s': %v", ref, err)
if swg2, ok := swaggerLoader.loadedRemoteSchemas[*parsedURL]; !ok {
if swg2, err = swaggerLoader.LoadSwaggerFromURI(resolvedPath); err != nil {
return nil, "", nil, fmt.Errorf("Error while resolving reference '%s': %v", ref, err)
}
swaggerLoader.loadedRemoteSchemas[*parsedURL] = swg2
}
swagger = swaggerLoader.loadedRemoteSchemas[*parsedURL]
ref = fmt.Sprintf("#%s", fragment)
componentPath = resolvedPath
}
Expand Down
35 changes: 33 additions & 2 deletions openapi3/swagger_loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,10 +307,10 @@ func TestLoadFromRemoteURL(t *testing.T) {

loader := openapi3.NewSwaggerLoader()
loader.IsExternalRefsAllowed = true
url, err := url.Parse("http://" + addr + "/test.openapi.json")
remote, err := url.Parse("http://" + addr + "/test.openapi.json")
require.NoError(t, err)

swagger, err := loader.LoadSwaggerFromURI(url)
swagger, err := loader.LoadSwaggerFromURI(remote)
require.NoError(t, err)

require.Equal(t, "string", swagger.Components.Schemas["TestSchema"].Value.Type)
Expand Down Expand Up @@ -423,3 +423,34 @@ func TestLoadYamlFileWithExternalSchemaRef(t *testing.T) {

require.NotNil(t, swagger.Components.Schemas["AnotherTestSchema"].Value.Type)
}

type hitCntFS struct {
fs http.Dir
hits map[string]int
}

func (fs hitCntFS) Open(fn string) (http.File, error) {
fs.hits[fn] = fs.hits[fn] + 1
return fs.fs.Open(fn)
}

func TestRemoteURLCaching(t *testing.T) {

sfs := hitCntFS{fs: "testdata", hits: map[string]int{}}
fs := http.FileServer(sfs)
ts := createTestServer(fs)
ts.Start()
defer ts.Close()

loader := openapi3.NewSwaggerLoader()
loader.IsExternalRefsAllowed = true
remote, err := url.Parse("http://" + addr + "/test.refcache.openapi.yml")
require.NoError(t, err)

_, err = loader.LoadSwaggerFromURI(remote)
require.NoError(t, err)

require.Contains(t, sfs.hits, "/test.refcache.openapi.yml")
require.Contains(t, sfs.hits, "/components.openapi.yml")
require.Equal(t, 1, sfs.hits["http://localhost:7965/components.openapi.yml"], "expcting 1 load of referenced schema")
}
15 changes: 15 additions & 0 deletions openapi3/testdata/test.refcache.openapi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
openapi: 3.0.0
info:
title: 'OAI Specification w/ refs in YAML. Multiple refs to the same remote schema'
version: '1'
paths: {}
components:
schemas:
AnotherTestSchema:
type: object
properties:
ref1:
"$ref": http://localhost:7965/components.openapi.yml#/components/schemas/CustomTestSchema
ref2:
"$ref": http://localhost:7965/components.openapi.yml#/components/schemas/Name

0 comments on commit bd0b49a

Please sign in to comment.