Skip to content

Commit df86906

Browse files
committed
refactor json $anchor using scope
1 parent 204e709 commit df86906

File tree

11 files changed

+188
-109
lines changed

11 files changed

+188
-109
lines changed

config/dynamic/config.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package dynamic
22

33
import (
44
"bytes"
5+
"fmt"
56
"github.com/Masterminds/sprig"
67
"mokapi/sortedmap"
78
"strings"
@@ -21,6 +22,7 @@ type Config struct {
2122
Data interface{}
2223
Refs Refs
2324
Listeners Listeners
25+
Scope *Scope
2426
}
2527

2628
type Refs struct {
@@ -46,6 +48,13 @@ func AddRef(parent, ref *Config) {
4648
parent.Info.Time = ref.Info.Time
4749
parent.Listeners.Invoke(parent)
4850
})
51+
52+
if ref.Scope == nil {
53+
ref.OpenScope("")
54+
}
55+
if parent.Scope != nil {
56+
ref.Scope.dynamic = parent.Scope.dynamic
57+
}
4958
}
5059

5160
func (l *Listeners) Add(key string, fn ConfigListener) {
@@ -146,3 +155,82 @@ func extractUsername(s string) string {
146155
slice := strings.Split(s, "\\")
147156
return slice[len(slice)-1]
148157
}
158+
159+
type Scope struct {
160+
Name string
161+
162+
stack []map[string]interface{}
163+
dynamic *Scope
164+
}
165+
166+
func NewScope(name string) *Scope {
167+
return &Scope{Name: name, stack: []map[string]interface{}{
168+
{},
169+
}}
170+
}
171+
172+
func (s *Scope) Get(name string) (interface{}, error) {
173+
if s == nil {
174+
return nil, fmt.Errorf("anchor '%s' not found: no scope present", name)
175+
}
176+
177+
if len(s.stack) != 0 {
178+
lexical := s.stack[len(s.stack)-1]
179+
if v, ok := lexical[name]; ok {
180+
return v, nil
181+
}
182+
}
183+
184+
return nil, fmt.Errorf("anchor '%s' not found in scope '%s", name, s.Name)
185+
}
186+
187+
func (s *Scope) Set(name string, value interface{}) error {
188+
if s == nil || len(s.stack) == 0 {
189+
return fmt.Errorf("set anchor '%s' failed: no scope present", name)
190+
}
191+
192+
lexical := s.stack[len(s.stack)-1]
193+
if _, ok := lexical[name]; ok {
194+
return fmt.Errorf("anchor '%s' already defined in scope '%s'", name, s.Name)
195+
}
196+
lexical[name] = value
197+
return nil
198+
}
199+
200+
func (s *Scope) GetDynamic(name string) (interface{}, error) {
201+
return s.dynamic.Get(name)
202+
}
203+
204+
func (s *Scope) SetDynamic(name string, value interface{}) error {
205+
return s.dynamic.Set(name, value)
206+
}
207+
208+
func (s *Scope) openScope(name string) {
209+
s.stack = append(s.stack, map[string]interface{}{})
210+
}
211+
212+
func (s *Scope) close() {
213+
s.stack = s.stack[:len(s.stack)-1]
214+
}
215+
216+
func (s *Scope) IsEmpty() bool {
217+
return len(s.stack) == 0
218+
}
219+
220+
func (c *Config) OpenScope(name string) {
221+
if c.Scope == nil {
222+
c.Scope = NewScope(name)
223+
} else {
224+
c.Scope.openScope(name)
225+
}
226+
}
227+
228+
func (c *Config) Close() {
229+
if c.Scope != nil {
230+
if c.Scope.IsEmpty() {
231+
c.Scope = nil
232+
} else {
233+
c.Scope.close()
234+
}
235+
}
236+
}

config/dynamic/dynamictest/reader.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ func (r *Reader) Read(u *url.URL, v any) (*dynamic.Config, error) {
1717
return nil, NotFound
1818
}
1919
if c, ok := r.Data[u.String()]; ok {
20+
if p, isParser := c.Data.(dynamic.Parser); isParser {
21+
if err := p.Parse(c, r); err != nil {
22+
return nil, err
23+
}
24+
return c, nil
25+
}
26+
2027
return c, nil
2128
}
2229
return nil, NotFound

config/dynamic/parse.go

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,15 @@ func Parse(c *Config, r Reader) error {
4242

4343
switch filepath.Ext(name) {
4444
case ".yml", ".yaml":
45-
d := &dynamicObject{data: c.Data}
46-
err := yaml.Unmarshal(data, d)
45+
err := yaml.Unmarshal(data, c)
4746
if err != nil {
4847
return err
4948
}
50-
c.Data = d.data
5149
case ".json":
52-
d := &dynamicObject{data: c.Data}
53-
err := UnmarshalJSON(data, d)
50+
err := UnmarshalJSON(data, c)
5451
if err != nil {
5552
return err
5653
}
57-
c.Data = d.data
5854
case ".lua", ".js", ".ts":
5955
if c.Data == nil {
6056
c.Data = script.New(name, data)
@@ -76,45 +72,45 @@ func Parse(c *Config, r Reader) error {
7672
return nil
7773
}
7874

79-
func (d *dynamicObject) UnmarshalJSON(b []byte) error {
75+
func (c *Config) UnmarshalJSON(b []byte) error {
8076
data := make(map[string]string)
8177
_ = UnmarshalJSON(b, &data)
8278

8379
if ct := getConfigType(data); ct != nil {
84-
d.data = reflect.New(ct.configType).Interface()
85-
err := UnmarshalJSON(b, d.data)
80+
c.Data = reflect.New(ct.configType).Interface()
81+
err := UnmarshalJSON(b, c.Data)
8682
if err != nil {
8783
return formatError(b, err)
8884
}
8985
return nil
9086
}
9187

92-
if d.data == nil {
88+
if c.Data == nil {
9389
return nil
9490
}
9591

96-
err := UnmarshalJSON(b, d.data)
92+
err := UnmarshalJSON(b, c.Data)
9793
if err != nil {
9894
return formatError(b, err)
9995
}
10096

10197
return nil
10298
}
10399

104-
func (d *dynamicObject) UnmarshalYAML(unmarshal func(interface{}) error) error {
100+
func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
105101
data := make(map[string]string)
106102
_ = unmarshal(data)
107103

108104
if ct := getConfigType(data); ct != nil {
109-
d.data = reflect.New(ct.configType).Interface()
110-
return unmarshal(d.data)
105+
c.Data = reflect.New(ct.configType).Interface()
106+
return unmarshal(c.Data)
111107
}
112108

113-
if d.data == nil {
109+
if c.Data == nil {
114110
return nil
115111
}
116112

117-
err := unmarshal(d.data)
113+
err := unmarshal(c.Data)
118114
if err != nil {
119115
return err
120116
}

config/dynamic/resolve.go

Lines changed: 6 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@ type PathResolver interface {
1414
Resolve(token string) (interface{}, error)
1515
}
1616

17-
type AnchorResolver interface {
18-
ResolveAnchor(anchor string, resolve func(anchor string, v interface{}) (interface{}, error)) (interface{}, error)
19-
}
20-
2117
type Converter interface {
2218
ConvertTo(i interface{}) (interface{}, error)
2319
}
@@ -26,7 +22,7 @@ func Resolve(ref string, element interface{}, config *Config, reader Reader) err
2622
var err error
2723

2824
if strings.HasPrefix(ref, "#") {
29-
err = resolveFragment(ref[1:], config.Data, element)
25+
err = resolveFragment(ref[1:], config, element)
3026
} else {
3127
var u *url.URL
3228
u, err = resolveUrl(ref, config)
@@ -47,7 +43,7 @@ func Resolve(ref string, element interface{}, config *Config, reader Reader) err
4743
if err != nil {
4844
return fmt.Errorf("resolve reference '%v' failed: %w", ref, err)
4945
}
50-
err = resolveFragment(u.Fragment, sub.Data, element)
46+
err = resolveFragment(u.Fragment, sub, element)
5147
AddRef(config, sub)
5248
}
5349

@@ -57,13 +53,14 @@ func Resolve(ref string, element interface{}, config *Config, reader Reader) err
5753
return nil
5854
}
5955

60-
func resolveFragment(fragment string, val interface{}, resolved interface{}) (err error) {
56+
func resolveFragment(fragment string, config *Config, resolved interface{}) (err error) {
57+
val := config.Data
6158
if fragment == "" {
6259
// resolve to current (root) element
6360
} else if strings.HasPrefix(fragment, "/") {
64-
val, err = resolvePath(fragment, val)
61+
val, err = resolvePath(fragment, config.Data)
6562
} else {
66-
val, err = resolveAnchor(fragment, val)
63+
val, err = config.Scope.Get(fragment)
6764
}
6865
if err != nil {
6966
return fmt.Errorf("resolve fragment '%v' failed: %w", fragment, err)
@@ -204,50 +201,6 @@ func resolvePath(path string, v interface{}) (interface{}, error) {
204201
return cursor, nil
205202
}
206203

207-
func resolveAnchor(anchor string, v interface{}) (interface{}, error) {
208-
if r, ok := v.(AnchorResolver); ok {
209-
return r.ResolveAnchor(anchor, resolveAnchor)
210-
}
211-
212-
val := reflect.Indirect(reflect.ValueOf(v))
213-
switch val.Kind() {
214-
case reflect.Struct:
215-
for i := 0; i < val.NumField(); i++ {
216-
f := val.Field(i)
217-
if !f.CanInterface() {
218-
continue
219-
}
220-
221-
json := val.Type().Field(i).Tag.Get("json")
222-
if strings.Split(json, ",")[0] == "$anchor" {
223-
if f.Interface() == anchor {
224-
return v, nil
225-
}
226-
}
227-
228-
if r, err := resolveAnchor(anchor, f.Interface()); err == nil {
229-
return r, nil
230-
}
231-
}
232-
case reflect.Map:
233-
if a := val.MapIndex(reflect.ValueOf("$anchor")); a.IsValid() {
234-
if a.Interface() == anchor {
235-
return v, nil
236-
}
237-
}
238-
239-
for _, k := range val.MapKeys() {
240-
if r, err := resolveAnchor(anchor, val.MapIndex(k).Interface()); err == nil {
241-
return r, nil
242-
}
243-
}
244-
default:
245-
return nil, fmt.Errorf("type '%v' not supported for anchor search", val.Kind())
246-
}
247-
248-
return nil, fmt.Errorf("anchor '%v' not found", anchor)
249-
}
250-
251204
func resolveUrl(ref string, cfg *Config) (*url.URL, error) {
252205
u, err := url.Parse(ref)
253206
if err != nil {

providers/openapi/response.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -142,13 +142,6 @@ func (r *Responses[K]) Resolve(token string) (interface{}, error) {
142142
return res.Value, nil
143143
}
144144

145-
func (r *Responses[k]) ResolveAnchor(anchor string, resolve func(string, interface{}) (interface{}, error)) (interface{}, error) {
146-
if r == nil {
147-
return nil, fmt.Errorf("unable to resolve %v", anchor)
148-
}
149-
return r.LinkedHashMap.ResolveAnchor(anchor, resolve)
150-
}
151-
152145
func (r *Responses[K]) GetResponse(httpStatus K) *Response {
153146
res, _ := r.Get(httpStatus)
154147
if res == nil {

providers/openapi/schema/schema.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,34 @@ func (s *Schema) Parse(config *dynamic.Config, reader dynamic.Reader) error {
9797
return nil
9898
}
9999

100+
if s.Id != "" {
101+
config.OpenScope(s.Id)
102+
} else if config.Scope == nil {
103+
if config.Info.Url != nil {
104+
config.OpenScope(config.Info.Url.String())
105+
} else if config.Scope == nil {
106+
config.OpenScope("")
107+
}
108+
}
109+
110+
if s.Anchor != "" {
111+
if err := config.Scope.Set(s.Anchor, s); err != nil {
112+
return err
113+
}
114+
}
115+
116+
for _, d := range s.Definitions {
117+
if err := d.Parse(config, reader); err != nil {
118+
return err
119+
}
120+
}
121+
122+
for _, d := range s.Defs {
123+
if err := d.Parse(config, reader); err != nil {
124+
return err
125+
}
126+
}
127+
100128
if err := s.Items.Parse(config, reader); err != nil {
101129
return err
102130
}

providers/openapi/schema/schemas.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,6 @@ func (s *Schemas) Resolve(token string) (interface{}, error) {
4545
return i.Value, nil
4646
}
4747

48-
func (s *Schemas) ResolveAnchor(anchor string, resolve func(string, interface{}) (interface{}, error)) (interface{}, error) {
49-
if s == nil {
50-
return nil, fmt.Errorf("unable to resolve %v", anchor)
51-
}
52-
return s.LinkedHashMap.ResolveAnchor(anchor, resolve)
53-
}
54-
5548
func (s *Schemas) UnmarshalJSON(b []byte) error {
5649
dec := json.NewDecoder(bytes.NewReader(b))
5750
token, err := dec.Token()

0 commit comments

Comments
 (0)