Skip to content

Commit f598766

Browse files
authored
Drop sl.LoadSwaggerFromURIFunc (#317)
1 parent ea43ca7 commit f598766

File tree

4 files changed

+108
-132
lines changed

4 files changed

+108
-132
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,8 @@ func arrayUniqueItemsChecker(items []interface{}) bool {
197197
// Check the uniqueness of the input slice(array in JSON)
198198
}
199199
```
200+
201+
## Sub-v0 breaking API changes
202+
203+
### v0.47.0
204+
Field `(*openapi3.SwaggerLoader).LoadSwaggerFromURIFunc` of type `func(*openapi3.SwaggerLoader, *url.URL) (*openapi3.Swagger, error)` was removed after the addition of the field `(*openapi3.SwaggerLoader).ReadFromURIFunc` of type `func(*openapi3.SwaggerLoader, *url.URL) ([]byte, error)`.

openapi3/swagger_loader.go

Lines changed: 53 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,14 @@ type SwaggerLoader struct {
2929
// IsExternalRefsAllowed enables visiting other files
3030
IsExternalRefsAllowed bool
3131

32-
// LoadSwaggerFromURIFunc allows overriding the swagger file/URL reading func
33-
LoadSwaggerFromURIFunc func(loader *SwaggerLoader, url *url.URL) (*Swagger, error)
34-
3532
// ReadFromURIFunc allows overriding the any file/URL reading func
3633
ReadFromURIFunc func(loader *SwaggerLoader, url *url.URL) ([]byte, error)
3734

3835
Context context.Context
3936

40-
visitedFiles map[string]struct{}
41-
visitedSwaggers map[string]*Swagger
37+
visitedPathItemRefs map[string]struct{}
38+
39+
visitedDocuments map[string]*Swagger
4240

4341
visitedExample map[*Example]struct{}
4442
visitedHeader map[*Header]struct{}
@@ -55,22 +53,17 @@ func NewSwaggerLoader() *SwaggerLoader {
5553
return &SwaggerLoader{}
5654
}
5755

58-
func (swaggerLoader *SwaggerLoader) reset() {
59-
swaggerLoader.visitedFiles = make(map[string]struct{})
60-
swaggerLoader.visitedSwaggers = make(map[string]*Swagger)
56+
func (swaggerLoader *SwaggerLoader) resetVisitedPathItemRefs() {
57+
swaggerLoader.visitedPathItemRefs = make(map[string]struct{})
6158
}
6259

6360
// LoadSwaggerFromURI loads a spec from a remote URL
6461
func (swaggerLoader *SwaggerLoader) LoadSwaggerFromURI(location *url.URL) (*Swagger, error) {
65-
swaggerLoader.reset()
62+
swaggerLoader.resetVisitedPathItemRefs()
6663
return swaggerLoader.loadSwaggerFromURIInternal(location)
6764
}
6865

6966
func (swaggerLoader *SwaggerLoader) loadSwaggerFromURIInternal(location *url.URL) (*Swagger, error) {
70-
f := swaggerLoader.LoadSwaggerFromURIFunc
71-
if f != nil {
72-
return f(swaggerLoader, location)
73-
}
7467
data, err := swaggerLoader.readURL(location)
7568
if err != nil {
7669
return nil, err
@@ -111,8 +104,7 @@ func (swaggerLoader *SwaggerLoader) loadSingleElementFromURI(ref string, rootPat
111104
}
112105

113106
func (swaggerLoader *SwaggerLoader) readURL(location *url.URL) ([]byte, error) {
114-
f := swaggerLoader.ReadFromURIFunc
115-
if f != nil {
107+
if f := swaggerLoader.ReadFromURIFunc; f != nil {
116108
return f(swaggerLoader, location)
117109
}
118110

@@ -121,36 +113,23 @@ func (swaggerLoader *SwaggerLoader) readURL(location *url.URL) ([]byte, error) {
121113
if err != nil {
122114
return nil, err
123115
}
124-
data, err := ioutil.ReadAll(resp.Body)
125116
defer resp.Body.Close()
126-
if err != nil {
127-
return nil, err
128-
}
129-
return data, nil
117+
return ioutil.ReadAll(resp.Body)
130118
}
131119
if location.Scheme != "" || location.Host != "" || location.RawQuery != "" {
132120
return nil, fmt.Errorf("unsupported URI: %q", location.String())
133121
}
134-
data, err := ioutil.ReadFile(location.Path)
135-
if err != nil {
136-
return nil, err
137-
}
138-
return data, nil
122+
return ioutil.ReadFile(location.Path)
139123
}
140124

141125
// LoadSwaggerFromFile loads a spec from a local file path
142126
func (swaggerLoader *SwaggerLoader) LoadSwaggerFromFile(path string) (*Swagger, error) {
143-
swaggerLoader.reset()
127+
swaggerLoader.resetVisitedPathItemRefs()
144128
return swaggerLoader.loadSwaggerFromFileInternal(path)
145129
}
146130

147131
func (swaggerLoader *SwaggerLoader) loadSwaggerFromFileInternal(path string) (*Swagger, error) {
148-
f := swaggerLoader.LoadSwaggerFromURIFunc
149132
pathAsURL := &url.URL{Path: path}
150-
if f != nil {
151-
x, err := f(swaggerLoader, pathAsURL)
152-
return x, err
153-
}
154133
data, err := swaggerLoader.readURL(pathAsURL)
155134
if err != nil {
156135
return nil, err
@@ -160,33 +139,39 @@ func (swaggerLoader *SwaggerLoader) loadSwaggerFromFileInternal(path string) (*S
160139

161140
// LoadSwaggerFromData loads a spec from a byte array
162141
func (swaggerLoader *SwaggerLoader) LoadSwaggerFromData(data []byte) (*Swagger, error) {
163-
swaggerLoader.reset()
142+
swaggerLoader.resetVisitedPathItemRefs()
164143
return swaggerLoader.loadSwaggerFromDataInternal(data)
165144
}
166145

167146
func (swaggerLoader *SwaggerLoader) loadSwaggerFromDataInternal(data []byte) (*Swagger, error) {
168-
swagger := &Swagger{}
169-
if err := yaml.Unmarshal(data, swagger); err != nil {
147+
doc := &Swagger{}
148+
if err := yaml.Unmarshal(data, doc); err != nil {
149+
return nil, err
150+
}
151+
if err := swaggerLoader.ResolveRefsIn(doc, nil); err != nil {
170152
return nil, err
171153
}
172-
return swagger, swaggerLoader.ResolveRefsIn(swagger, nil)
154+
return doc, nil
173155
}
174156

175157
// LoadSwaggerFromDataWithPath takes the OpenApi spec data in bytes and a path where the resolver can find referred
176158
// elements and returns a *Swagger with all resolved data or an error if unable to load data or resolve refs.
177159
func (swaggerLoader *SwaggerLoader) LoadSwaggerFromDataWithPath(data []byte, path *url.URL) (*Swagger, error) {
178-
swaggerLoader.reset()
160+
swaggerLoader.resetVisitedPathItemRefs()
179161
return swaggerLoader.loadSwaggerFromDataWithPathInternal(data, path)
180162
}
181163

182164
func (swaggerLoader *SwaggerLoader) loadSwaggerFromDataWithPathInternal(data []byte, path *url.URL) (*Swagger, error) {
183-
visited, ok := swaggerLoader.visitedSwaggers[path.String()]
184-
if ok {
185-
return visited, nil
165+
if swaggerLoader.visitedDocuments == nil {
166+
swaggerLoader.visitedDocuments = make(map[string]*Swagger)
167+
}
168+
uri := path.String()
169+
if doc, ok := swaggerLoader.visitedDocuments[uri]; ok {
170+
return doc, nil
186171
}
187172

188173
swagger := &Swagger{}
189-
swaggerLoader.visitedSwaggers[path.String()] = swagger
174+
swaggerLoader.visitedDocuments[uri] = swagger
190175

191176
if err := yaml.Unmarshal(data, swagger); err != nil {
192177
return nil, err
@@ -200,32 +185,8 @@ func (swaggerLoader *SwaggerLoader) loadSwaggerFromDataWithPathInternal(data []b
200185

201186
// ResolveRefsIn expands references if for instance spec was just unmarshalled
202187
func (swaggerLoader *SwaggerLoader) ResolveRefsIn(swagger *Swagger, path *url.URL) (err error) {
203-
if swaggerLoader.visitedExample == nil {
204-
swaggerLoader.visitedExample = make(map[*Example]struct{})
205-
}
206-
if swaggerLoader.visitedHeader == nil {
207-
swaggerLoader.visitedHeader = make(map[*Header]struct{})
208-
}
209-
if swaggerLoader.visitedLink == nil {
210-
swaggerLoader.visitedLink = make(map[*Link]struct{})
211-
}
212-
if swaggerLoader.visitedParameter == nil {
213-
swaggerLoader.visitedParameter = make(map[*Parameter]struct{})
214-
}
215-
if swaggerLoader.visitedRequestBody == nil {
216-
swaggerLoader.visitedRequestBody = make(map[*RequestBody]struct{})
217-
}
218-
if swaggerLoader.visitedResponse == nil {
219-
swaggerLoader.visitedResponse = make(map[*Response]struct{})
220-
}
221-
if swaggerLoader.visitedSchema == nil {
222-
swaggerLoader.visitedSchema = make(map[*Schema]struct{})
223-
}
224-
if swaggerLoader.visitedSecurityScheme == nil {
225-
swaggerLoader.visitedSecurityScheme = make(map[*SecurityScheme]struct{})
226-
}
227-
if swaggerLoader.visitedFiles == nil {
228-
swaggerLoader.reset()
188+
if swaggerLoader.visitedPathItemRefs == nil {
189+
swaggerLoader.resetVisitedPathItemRefs()
229190
}
230191

231192
// Visit all components
@@ -456,6 +417,9 @@ func (swaggerLoader *SwaggerLoader) resolveRefSwagger(swagger *Swagger, ref stri
456417

457418
func (swaggerLoader *SwaggerLoader) resolveHeaderRef(swagger *Swagger, component *HeaderRef, documentPath *url.URL) error {
458419
if component != nil && component.Value != nil {
420+
if swaggerLoader.visitedHeader == nil {
421+
swaggerLoader.visitedHeader = make(map[*Header]struct{})
422+
}
459423
if _, ok := swaggerLoader.visitedHeader[component.Value]; ok {
460424
return nil
461425
}
@@ -500,6 +464,9 @@ func (swaggerLoader *SwaggerLoader) resolveHeaderRef(swagger *Swagger, component
500464

501465
func (swaggerLoader *SwaggerLoader) resolveParameterRef(swagger *Swagger, component *ParameterRef, documentPath *url.URL) error {
502466
if component != nil && component.Value != nil {
467+
if swaggerLoader.visitedParameter == nil {
468+
swaggerLoader.visitedParameter = make(map[*Parameter]struct{})
469+
}
503470
if _, ok := swaggerLoader.visitedParameter[component.Value]; ok {
504471
return nil
505472
}
@@ -560,6 +527,9 @@ func (swaggerLoader *SwaggerLoader) resolveParameterRef(swagger *Swagger, compon
560527

561528
func (swaggerLoader *SwaggerLoader) resolveRequestBodyRef(swagger *Swagger, component *RequestBodyRef, documentPath *url.URL) error {
562529
if component != nil && component.Value != nil {
530+
if swaggerLoader.visitedRequestBody == nil {
531+
swaggerLoader.visitedRequestBody = make(map[*RequestBody]struct{})
532+
}
563533
if _, ok := swaggerLoader.visitedRequestBody[component.Value]; ok {
564534
return nil
565535
}
@@ -612,6 +582,9 @@ func (swaggerLoader *SwaggerLoader) resolveRequestBodyRef(swagger *Swagger, comp
612582

613583
func (swaggerLoader *SwaggerLoader) resolveResponseRef(swagger *Swagger, component *ResponseRef, documentPath *url.URL) error {
614584
if component != nil && component.Value != nil {
585+
if swaggerLoader.visitedResponse == nil {
586+
swaggerLoader.visitedResponse = make(map[*Response]struct{})
587+
}
615588
if _, ok := swaggerLoader.visitedResponse[component.Value]; ok {
616589
return nil
617590
}
@@ -683,6 +656,9 @@ func (swaggerLoader *SwaggerLoader) resolveResponseRef(swagger *Swagger, compone
683656

684657
func (swaggerLoader *SwaggerLoader) resolveSchemaRef(swagger *Swagger, component *SchemaRef, documentPath *url.URL) error {
685658
if component != nil && component.Value != nil {
659+
if swaggerLoader.visitedSchema == nil {
660+
swaggerLoader.visitedSchema = make(map[*Schema]struct{})
661+
}
686662
if _, ok := swaggerLoader.visitedSchema[component.Value]; ok {
687663
return nil
688664
}
@@ -766,6 +742,9 @@ func (swaggerLoader *SwaggerLoader) resolveSchemaRef(swagger *Swagger, component
766742

767743
func (swaggerLoader *SwaggerLoader) resolveSecuritySchemeRef(swagger *Swagger, component *SecuritySchemeRef, documentPath *url.URL) error {
768744
if component != nil && component.Value != nil {
745+
if swaggerLoader.visitedSecurityScheme == nil {
746+
swaggerLoader.visitedSecurityScheme = make(map[*SecurityScheme]struct{})
747+
}
769748
if _, ok := swaggerLoader.visitedSecurityScheme[component.Value]; ok {
770749
return nil
771750
}
@@ -801,6 +780,9 @@ func (swaggerLoader *SwaggerLoader) resolveSecuritySchemeRef(swagger *Swagger, c
801780

802781
func (swaggerLoader *SwaggerLoader) resolveExampleRef(swagger *Swagger, component *ExampleRef, documentPath *url.URL) error {
803782
if component != nil && component.Value != nil {
783+
if swaggerLoader.visitedExample == nil {
784+
swaggerLoader.visitedExample = make(map[*Example]struct{})
785+
}
804786
if _, ok := swaggerLoader.visitedExample[component.Value]; ok {
805787
return nil
806788
}
@@ -836,6 +818,9 @@ func (swaggerLoader *SwaggerLoader) resolveExampleRef(swagger *Swagger, componen
836818

837819
func (swaggerLoader *SwaggerLoader) resolveLinkRef(swagger *Swagger, component *LinkRef, documentPath *url.URL) error {
838820
if component != nil && component.Value != nil {
821+
if swaggerLoader.visitedLink == nil {
822+
swaggerLoader.visitedLink = make(map[*Link]struct{})
823+
}
839824
if _, ok := swaggerLoader.visitedLink[component.Value]; ok {
840825
return nil
841826
}
@@ -875,10 +860,10 @@ func (swaggerLoader *SwaggerLoader) resolvePathItemRef(swagger *Swagger, entrypo
875860
key = documentPath.EscapedPath()
876861
}
877862
key += entrypoint
878-
if _, ok := swaggerLoader.visitedFiles[key]; ok {
863+
if _, ok := swaggerLoader.visitedPathItemRefs[key]; ok {
879864
return nil
880865
}
881-
swaggerLoader.visitedFiles[key] = struct{}{}
866+
swaggerLoader.visitedPathItemRefs[key] = struct{}{}
882867

883868
const prefix = "#/paths/"
884869
if pathItem == nil {

openapi3/swagger_loader_read_from_uri_func_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package openapi3
22

33
import (
4+
"fmt"
45
"io/ioutil"
56
"net/url"
67
"path/filepath"
@@ -21,3 +22,52 @@ func TestLoaderReadFromURIFunc(t *testing.T) {
2122
require.NoError(t, doc.Validate(loader.Context))
2223
require.Equal(t, "bar", doc.Paths["/foo"].Get.Responses.Get(200).Value.Content.Get("application/json").Schema.Value.Properties["foo"].Value.Properties["bar"].Value.Items.Value.Example)
2324
}
25+
26+
type multipleSourceSwaggerLoaderExample struct {
27+
Sources map[string][]byte
28+
}
29+
30+
func (l *multipleSourceSwaggerLoaderExample) LoadSwaggerFromURI(
31+
loader *SwaggerLoader,
32+
location *url.URL,
33+
) ([]byte, error) {
34+
source := l.resolveSourceFromURI(location)
35+
if source == nil {
36+
return nil, fmt.Errorf("Unsupported URI: %q", location.String())
37+
}
38+
return source, nil
39+
}
40+
41+
func (l *multipleSourceSwaggerLoaderExample) resolveSourceFromURI(location fmt.Stringer) []byte {
42+
return l.Sources[location.String()]
43+
}
44+
45+
func TestResolveSchemaExternalRef(t *testing.T) {
46+
rootLocation := &url.URL{Scheme: "http", Host: "example.com", Path: "spec.json"}
47+
externalLocation := &url.URL{Scheme: "http", Host: "example.com", Path: "external.json"}
48+
rootSpec := []byte(fmt.Sprintf(
49+
`{"openapi":"3.0.0","info":{"title":"MyAPI","version":"0.1","description":"An API"},"paths":{},"components":{"schemas":{"Root":{"allOf":[{"$ref":"%s#/components/schemas/External"}]}}}}`,
50+
externalLocation.String(),
51+
))
52+
externalSpec := []byte(`{"openapi":"3.0.0","info":{"title":"MyAPI","version":"0.1","description":"External Spec"},"paths":{},"components":{"schemas":{"External":{"type":"string"}}}}`)
53+
multipleSourceLoader := &multipleSourceSwaggerLoaderExample{
54+
Sources: map[string][]byte{
55+
rootLocation.String(): rootSpec,
56+
externalLocation.String(): externalSpec,
57+
},
58+
}
59+
loader := &SwaggerLoader{
60+
IsExternalRefsAllowed: true,
61+
ReadFromURIFunc: multipleSourceLoader.LoadSwaggerFromURI,
62+
}
63+
64+
doc, err := loader.LoadSwaggerFromURI(rootLocation)
65+
require.NoError(t, err)
66+
67+
err = doc.Validate(loader.Context)
68+
require.NoError(t, err)
69+
70+
refRootVisited := doc.Components.Schemas["Root"].Value.AllOf[0]
71+
require.Equal(t, fmt.Sprintf("%s#/components/schemas/External", externalLocation.String()), refRootVisited.Ref)
72+
require.NotNil(t, refRootVisited.Value)
73+
}

0 commit comments

Comments
 (0)