Skip to content

Commit 73e3f6b

Browse files
committed
encoding/openapi: bug fixes in expandReference
- indirect the reference, but avoid premature expansion of a disjunction - intercept infinite recursion so that a proper error message (with location) can be given. - allow "type" to be unspecified. This was incorrectly assumed to be a required field. Issue #56 Change-Id: Icc543cb626e3533305e73a0867a567c19daf8d2e Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2682 Reviewed-by: Marcel van Lohuizen <[email protected]>
1 parent d8f4450 commit 73e3f6b

File tree

4 files changed

+146
-15
lines changed

4 files changed

+146
-15
lines changed

encoding/openapi/build.go

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type buildContext struct {
3939
nameFunc func(inst *cue.Instance, path []string) string
4040
descFunc func(v cue.Value) string
4141
fieldFilter *regexp.Regexp
42+
evalDepth int // detect cycles when resolving references
4243

4344
schemas *OrderedMap
4445

@@ -142,11 +143,6 @@ func (c *buildContext) isInternal(name string) bool {
142143
return strings.HasSuffix(name, "_value")
143144
}
144145

145-
// shouldExpand reports is the given identifier is not exported.
146-
func (c *buildContext) shouldExpand(p *cue.Instance, ref []string) bool {
147-
return c.expandRefs
148-
}
149-
150146
func (b *builder) failf(v cue.Value, format string, args ...interface{}) {
151147
panic(&openapiError{
152148
errors.NewMessage(format, args),
@@ -187,13 +183,31 @@ func (b *builder) schema(name string, v cue.Value) *oaSchema {
187183
return schema
188184
}
189185

186+
func resolve(v cue.Value) cue.Value {
187+
switch op, a := v.Expr(); op {
188+
case cue.SelectorOp:
189+
field, _ := a[1].String()
190+
v = resolve(a[0]).Lookup(field)
191+
}
192+
return v
193+
}
194+
190195
func (b *builder) value(v cue.Value, f typeFunc) (isRef bool) {
191196
count := 0
192197
disallowDefault := false
193198
var values cue.Value
194-
p, a := v.Reference()
195-
if b.ctx.shouldExpand(p, a) {
196-
values = v
199+
if b.ctx.expandRefs {
200+
// Cycles are not allowed when expanding references. Right now we just
201+
// cap the depth of evaluation at 30.
202+
// TODO: do something more principled.
203+
const maxDepth = 30
204+
if b.ctx.evalDepth > maxDepth {
205+
b.failf(v, "maximum stack depth of %d reached", maxDepth)
206+
}
207+
b.ctx.evalDepth++
208+
defer func() { b.ctx.evalDepth-- }()
209+
210+
values = resolve(v)
197211
count = 1
198212
} else {
199213
dedup := map[string]bool{}
@@ -844,12 +858,10 @@ func (b *builder) setNot(key string, value interface{}) {
844858
func (b *builder) finish() *oaSchema {
845859
switch len(b.allOf) {
846860
case 0:
847-
if b.typ == "" {
848-
b.failf(cue.Value{}, "no type specified at finish")
849-
return nil
850-
}
851861
t := &OrderedMap{}
852-
setType(t, b)
862+
if b.typ != "" {
863+
setType(t, b)
864+
}
853865
return t
854866

855867
case 1:

encoding/openapi/openapi.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ type Generator struct {
4949
FieldFilter string
5050

5151
// ExpandReferences replaces references with actual objects when generating
52-
// OpenAPI Schema. It is an error for an CUE value to refer to itself.
52+
// OpenAPI Schema. It is an error for an CUE value to refer to itself
53+
// if this option is used.
5354
ExpandReferences bool
5455
}
5556

encoding/openapi/openapi_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ func TestParseDefinitions(t *testing.T) {
6262
"oneof.cue",
6363
"oneof.json",
6464
defaultConfig,
65+
}, {
66+
"oneof.cue",
67+
"oneof-resolve.json",
68+
resolveRefs,
6569
}, {
6670
"openapi.cue",
6771
"openapi.json",
@@ -107,7 +111,7 @@ func TestParseDefinitions(t *testing.T) {
107111
t.Fatal(err)
108112
}
109113

110-
if d := diff.Diff(string(b), out.String()); d != "" {
114+
if d := diff.Diff(out.String(), string(b)); d != "" {
111115
t.Errorf("files differ:\n%v", d)
112116
}
113117
})
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
{
2+
"openapi": "3.0.0",
3+
"info": {
4+
"title": "test",
5+
"version": "v1"
6+
},
7+
"components": {
8+
"schemas": {
9+
"MyString": {
10+
"oneOf": [
11+
{
12+
"type": "object",
13+
"required": [
14+
"exact"
15+
],
16+
"properties": {
17+
"exact": {
18+
"type": "string",
19+
"format": "string"
20+
}
21+
}
22+
},
23+
{
24+
"type": "object",
25+
"required": [
26+
"regex"
27+
],
28+
"properties": {
29+
"regex": {
30+
"type": "string",
31+
"format": "string"
32+
}
33+
}
34+
}
35+
]
36+
},
37+
"MyInt": {
38+
"type": "integer"
39+
},
40+
"Foo": {
41+
"type": "object",
42+
"required": [
43+
"include",
44+
"exclude",
45+
"count"
46+
],
47+
"properties": {
48+
"include": {
49+
"oneOf": [
50+
{
51+
"type": "object",
52+
"required": [
53+
"exact"
54+
],
55+
"properties": {
56+
"exact": {
57+
"type": "string",
58+
"format": "string"
59+
}
60+
}
61+
},
62+
{
63+
"type": "object",
64+
"required": [
65+
"regex"
66+
],
67+
"properties": {
68+
"regex": {
69+
"type": "string",
70+
"format": "string"
71+
}
72+
}
73+
}
74+
]
75+
},
76+
"exclude": {
77+
"type": "array",
78+
"items": {
79+
"oneOf": [
80+
{
81+
"type": "object",
82+
"required": [
83+
"exact"
84+
],
85+
"properties": {
86+
"exact": {
87+
"type": "string",
88+
"format": "string"
89+
}
90+
}
91+
},
92+
{
93+
"type": "object",
94+
"required": [
95+
"regex"
96+
],
97+
"properties": {
98+
"regex": {
99+
"type": "string",
100+
"format": "string"
101+
}
102+
}
103+
}
104+
]
105+
}
106+
},
107+
"count": {
108+
"type": "integer"
109+
}
110+
}
111+
}
112+
}
113+
}
114+
}

0 commit comments

Comments
 (0)