Skip to content
This repository has been archived by the owner on Nov 18, 2021. It is now read-only.

Commit

Permalink
encoding/openapi: allow empty string from ReferenceFunc
Browse files Browse the repository at this point in the history
When users use ReferenceFunc, it may be hard to
map certain types, like CUE builtin types, to the custom
namespace. Returning "" now allows such references
to be expanded in place. In the case of CUE builtin types,
the represented type will be that of the underlying type
(usually a string).

Change-Id: I27b5bd749f2c5339a2e5381376333e77424ee682
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2760
Reviewed-by: Jason Wang <jasonwzm@google.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
  • Loading branch information
mpvl committed Aug 8, 2019
1 parent dc30df1 commit ceb003d
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 22 deletions.
50 changes: 29 additions & 21 deletions encoding/openapi/build.go
Expand Up @@ -109,7 +109,11 @@ func schemas(g *Generator, inst *cue.Instance) (schemas *OrderedMap, err error)
if c.isInternal(label) {
continue
}
c.schemas.Set(c.makeRef(inst, []string{label}), c.build(label, i.Value()))
ref := c.makeRef(inst, []string{label})
if ref == "" {
continue
}
c.schemas.Set(ref, c.build(label, i.Value()))
}

// keep looping until a fixed point is reached.
Expand Down Expand Up @@ -196,11 +200,21 @@ func (b *builder) schema(name string, v cue.Value) *oaSchema {
return schema
}

func resolve(v cue.Value) cue.Value {
func (b *builder) resolve(v cue.Value) cue.Value {
// Cycles are not allowed when expanding references. Right now we just
// cap the depth of evaluation at 30.
// TODO: do something more principled.
const maxDepth = 30
if b.ctx.evalDepth > maxDepth {
b.failf(v, "maximum stack depth of %d reached", maxDepth)
}
b.ctx.evalDepth++
defer func() { b.ctx.evalDepth-- }()

switch op, a := v.Expr(); op {
case cue.SelectorOp:
field, _ := a[1].String()
v = resolve(a[0]).Lookup(field)
v = b.resolve(a[0]).Lookup(field)
}
return v
}
Expand All @@ -210,17 +224,7 @@ func (b *builder) value(v cue.Value, f typeFunc) (isRef bool) {
disallowDefault := false
var values cue.Value
if b.ctx.expandRefs || b.format != "" {
// Cycles are not allowed when expanding references. Right now we just
// cap the depth of evaluation at 30.
// TODO: do something more principled.
const maxDepth = 30
if b.ctx.evalDepth > maxDepth {
b.failf(v, "maximum stack depth of %d reached", maxDepth)
}
b.ctx.evalDepth++
defer func() { b.ctx.evalDepth-- }()

values = resolve(v)
values = b.resolve(v)
count = 1
} else {
dedup := map[string]bool{}
Expand All @@ -231,18 +235,22 @@ func (b *builder) value(v cue.Value, f typeFunc) (isRef bool) {
switch p, r := v.Reference(); {
case len(r) > 0:
ref := b.ctx.makeRef(p, r)
if ref == "" {
v = b.resolve(v)
break
}
if dedup[ref] {
continue
}
dedup[ref] = true

b.addRef(v, p, r)
disallowDefault = true
default:
hasNoRef = true
count++
values = values.Unify(v)
continue
}
hasNoRef = true
count++
values = values.Unify(v)
}
isRef = !hasNoRef && len(dedup) == 1
}
Expand Down Expand Up @@ -502,7 +510,7 @@ func (b *builder) object(v cue.Value) {
// TODO: extract format from specific type.

default:
b.failf(v, "unsupported op %v for number type", op)
b.failf(v, "unsupported op %v for object type (%v)", op, v)
return
}

Expand Down Expand Up @@ -582,7 +590,7 @@ func (b *builder) array(v cue.Value) {
// TODO: extract format from specific type.

default:
b.failf(v, "unsupported op %v for number type", op)
b.failf(v, "unsupported op %v for array type", op)
return
}

Expand Down Expand Up @@ -698,7 +706,7 @@ func (b *builder) number(v cue.Value) {
// TODO: extract format from specific type.

default:
// panic(fmt.Sprintf("unsupported of %v for number type", op))
b.failf(v, "unsupported op for number %v", op)
}
}

Expand Down
3 changes: 2 additions & 1 deletion encoding/openapi/openapi.go
Expand Up @@ -27,7 +27,8 @@ type Generator struct {
Info OrderedMap

// ReferenceFunc allows users to specify an alternative representation
// for references.
// for references. An empty string tells the generator to expand the type
// in place and not generate a schema for that entity, if applicable.
ReferenceFunc func(inst *cue.Instance, path []string) string

// DescriptionFunc allows rewriting a description associated with a certain
Expand Down
13 changes: 13 additions & 0 deletions encoding/openapi/openapi_test.go
Expand Up @@ -94,6 +94,19 @@ func TestParseDefinitions(t *testing.T) {
return "Randomly picked description from a set of size one."
},
},
}, {
"refs.cue",
"refs.json",
&Generator{
Info: info,
ReferenceFunc: func(inst *cue.Instance, path []string) string {
switch {
case strings.HasPrefix(path[0], "Excluded"):
return ""
}
return strings.Join(path, ".")
},
},
}}
for _, tc := range testCases {
t.Run(tc.out, func(t *testing.T) {
Expand Down
13 changes: 13 additions & 0 deletions encoding/openapi/testdata/refs.cue
@@ -0,0 +1,13 @@
Keep: {
// This comment is included
excludedStruct: ExcludedStruct
excludedInt: ExcludedInt
}

// ExcludedStruct is not included in the output.
ExcludedStruct: {
A: int
}

// ExcludedInt is not included in the output.
ExcludedInt: int
35 changes: 35 additions & 0 deletions encoding/openapi/testdata/refs.json
@@ -0,0 +1,35 @@
{
"openapi": "3.0.0",
"info": {
"title": "test",
"version": "v1"
},
"components": {
"schemas": {
"Keep": {
"type": "object",
"required": [
"excludedStruct",
"excludedInt"
],
"properties": {
"excludedStruct": {
"description": "This comment is included",
"type": "object",
"required": [
"A"
],
"properties": {
"A": {
"type": "integer"
}
}
},
"excludedInt": {
"type": "integer"
}
}
}
}
}
}

0 comments on commit ceb003d

Please sign in to comment.