forked from snyk/vervet
-
Notifications
You must be signed in to change notification settings - Fork 0
/
localizer.go
130 lines (119 loc) · 3.97 KB
/
localizer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package vervet
import (
"log"
"path"
"reflect"
"strings"
"github.com/getkin/kin-openapi/openapi3"
"github.com/mitchellh/reflectwalk"
)
// Localize rewrites all references in an OpenAPI document to local references.
func Localize(doc *Document) error {
l := newLocalizer(doc.T)
err := l.localize()
if err != nil {
return err
}
return doc.ResolveRefs()
}
// localizer rewrites references in an OpenAPI document object to local
// references, so that the spec is self-contained.
type localizer struct {
doc *openapi3.T
curRefType reflect.Value
curRefField reflect.Value
curValueField reflect.Value
}
// newLocalizer returns a new localizer.
func newLocalizer(doc *openapi3.T) *localizer {
return &localizer{doc: doc}
}
// localize rewrites all references in the OpenAPI document to local references.
func (l *localizer) localize() error {
err := reflectwalk.Walk(l.doc, l)
if err != nil {
return err
}
// Some of the localized components may have non-localized references,
// since they were just added to the document object tree in the prior
// walk. Brute-forcing them into the fold...
return reflectwalk.Walk(l.doc.Components, l)
}
// Struct implements reflectwalk.StructWalker
func (l *localizer) Struct(v reflect.Value) error {
l.curRefType, l.curRefField, l.curValueField = v, v.FieldByName("Ref"), v.FieldByName("Value")
return nil
}
// StructField implements reflectwalk.StructWalker
func (l *localizer) StructField(sf reflect.StructField, v reflect.Value) error {
if !l.curRefField.IsValid() || !l.curValueField.IsValid() {
return nil
}
refPath := l.curRefField.String()
if refPath == "" {
return nil
}
// TODO: Resolve unique names from external component refs, URI basename
// may not be good enough.
refBase := path.Base(refPath)
if isLocalRef(refPath) {
return nil
}
switch refObj := l.curRefType.Addr().Interface().(type) {
case *openapi3.SchemaRef:
refObj.Ref = "#/components/schemas/" + refBase
if l.doc.Components.Schemas == nil {
l.doc.Components.Schemas = map[string]*openapi3.SchemaRef{}
}
if l.doc.Components.Schemas[refBase] == nil {
l.doc.Components.Schemas[refBase] = &openapi3.SchemaRef{Value: refObj.Value}
}
case *openapi3.ParameterRef:
refObj.Ref = "#/components/parameters/" + refBase
if l.doc.Components.Parameters == nil {
l.doc.Components.Parameters = map[string]*openapi3.ParameterRef{}
}
if l.doc.Components.Parameters[refBase] == nil {
l.doc.Components.Parameters[refBase] = &openapi3.ParameterRef{Value: refObj.Value}
}
case *openapi3.LinkRef:
refObj.Ref = "#/components/links/" + refBase
if l.doc.Components.Links == nil {
l.doc.Components.Links = map[string]*openapi3.LinkRef{}
}
if l.doc.Components.Links[refBase] == nil {
l.doc.Components.Links[refBase] = &openapi3.LinkRef{Value: refObj.Value}
}
case *openapi3.RequestBodyRef:
refObj.Ref = "#/components/requests/" + refBase
if l.doc.Components.RequestBodies == nil {
l.doc.Components.RequestBodies = map[string]*openapi3.RequestBodyRef{}
}
if l.doc.Components.RequestBodies[refBase] == nil {
l.doc.Components.RequestBodies[refBase] = &openapi3.RequestBodyRef{Value: refObj.Value}
}
case *openapi3.ResponseRef:
refObj.Ref = "#/components/responses/" + refBase
if l.doc.Components.Responses == nil {
l.doc.Components.Responses = map[string]*openapi3.ResponseRef{}
}
if l.doc.Components.Responses[refBase] == nil {
l.doc.Components.Responses[refBase] = &openapi3.ResponseRef{Value: refObj.Value}
}
case *openapi3.HeaderRef:
refObj.Ref = "#/components/headers/" + refBase
if l.doc.Components.Headers == nil {
l.doc.Components.Headers = map[string]*openapi3.HeaderRef{}
}
if l.doc.Components.Headers[refBase] == nil {
l.doc.Components.Headers[refBase] = &openapi3.HeaderRef{Value: refObj.Value}
}
default:
log.Printf("warning, unsupported ref type %T", refObj)
}
return nil
}
// isLocalRef returns whether the reference is localized.
func isLocalRef(s string) bool {
return strings.HasPrefix(s, "#/components/")
}