encoding/openapi: change naming in line with standard
Issue #56
Change-Id: I382b1a6d35d31ec42a9bdfe8b85037b78b518166
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2441
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/encoding/openapi/build.go b/encoding/openapi/build.go
index 10f93ef..6b83e78 100644
--- a/encoding/openapi/build.go
+++ b/encoding/openapi/build.go
@@ -36,7 +36,7 @@
expandRefs bool
nameFunc func(inst *cue.Instance, path []string) string
- schemas *orderedMap
+ schemas *OrderedMap
// Track external schemas.
externalRefs map[string]*externalType
@@ -49,17 +49,17 @@
value cue.Value
}
-type oaSchema = orderedMap
+type oaSchema = OrderedMap
type typeFunc func(b *builder, a cue.Value)
-func components(inst *cue.Instance, cfg *Config) (comps *orderedMap, err error) {
+func schemas(g *Generator, inst *cue.Instance) (schemas OrderedMap, err error) {
c := buildContext{
inst: inst,
refPrefix: "components/schema",
- expandRefs: cfg.ExpandReferences,
- nameFunc: cfg.ReferenceFunc,
- schemas: &orderedMap{},
+ expandRefs: g.ExpandReferences,
+ nameFunc: g.ReferenceFunc,
+ schemas: &OrderedMap{},
externalRefs: map[string]*externalType{},
}
@@ -77,11 +77,7 @@
}
}()
- schemas := &orderedMap{}
- schemas.Set("schema", c.schemas)
- comps = &orderedMap{}
- comps.Set("openapi", "3.0.0")
- comps.Set("components", schemas)
+ // Although paths is empty for now, it makes it valid OpenAPI spec.
i, err := inst.Value().Fields()
if err != nil {
@@ -93,7 +89,7 @@
if c.isInternal(label) {
continue
}
- c.schemas.Set(c.makeRef(inst, []string{label}), c.build(label, i.Value()))
+ c.schemas.set(c.makeRef(inst, []string{label}), c.build(label, i.Value()))
}
// keep looping until a fixed point is reached.
@@ -109,10 +105,11 @@
for _, k := range external {
ext := c.externalRefs[k]
- c.schemas.Set(ext.ref, c.build(ext.ref, ext.value.Eval()))
+ c.schemas.set(ext.ref, c.build(ext.ref, ext.value.Eval()))
}
}
- return comps, nil
+
+ return *c.schemas, nil
}
func (c *buildContext) build(name string, v cue.Value) *oaSchema {
@@ -156,7 +153,7 @@
}
if len(doc) > 0 {
str := strings.TrimSpace(strings.Join(doc, "\n"))
- schema.Prepend("description", str)
+ schema.prepend("description", str)
}
}
@@ -721,35 +718,35 @@
func setType(t *oaSchema, b *builder) {
if b.typ != "" {
- t.Set("type", b.typ)
+ t.set("type", b.typ)
if b.format != "" {
- t.Set("format", b.format)
+ t.set("format", b.format)
}
}
}
func (b *builder) set(key string, v interface{}) {
if b.current == nil {
- b.current = &orderedMap{}
+ b.current = &OrderedMap{}
b.allOf = append(b.allOf, b.current)
setType(b.current, b)
- } else if b.current.Exists(key) {
- b.current = &orderedMap{}
+ } else if b.current.exists(key) {
+ b.current = &OrderedMap{}
b.allOf = append(b.allOf, b.current)
}
- b.current.Set(key, v)
+ b.current.set(key, v)
}
func (b *builder) kv(key string, value interface{}) *oaSchema {
- constraint := &orderedMap{}
+ constraint := &OrderedMap{}
setType(constraint, b)
- constraint.Set(key, value)
+ constraint.set(key, value)
return constraint
}
func (b *builder) setNot(key string, value interface{}) {
- not := &orderedMap{}
- not.Set("not", b.kv(key, value))
+ not := &OrderedMap{}
+ not.set("not", b.kv(key, value))
b.add(not)
}
@@ -760,7 +757,7 @@
b.failf(cue.Value{}, "no type specified at finish")
return nil
}
- t := &orderedMap{}
+ t := &OrderedMap{}
setType(t, b)
return t
@@ -769,8 +766,8 @@
return b.allOf[0]
default:
- t := &orderedMap{}
- t.Set("allOf", b.allOf)
+ t := &OrderedMap{}
+ t.set("allOf", b.allOf)
return t
}
}
diff --git a/encoding/openapi/openapi.go b/encoding/openapi/openapi.go
index b1a4628..8d518a7 100644
--- a/encoding/openapi/openapi.go
+++ b/encoding/openapi/openapi.go
@@ -20,8 +20,8 @@
"cuelang.org/go/cue"
)
-// A Config defines options for mapping CUE to and from OpenAPI.
-type Config struct {
+// A Generator converts CUE to OpenAPI.
+type Generator struct {
// ReferenceFunc allows users to specify an alternative representation
// for references.
ReferenceFunc func(inst *cue.Instance, path []string) string
@@ -31,23 +31,55 @@
SelfContained bool
// ExpandReferences replaces references with actual objects when generating
- // OpenAPI Schema. It is an error for an CUE value to refer to itself
- // when this object is used.
+ // OpenAPI Schema. It is an error for an CUE value to refer to itself.
ExpandReferences bool
}
-// Gen generates the set OpenAPI schema for all top-level types of the given
-// instance.
+// Config is now Generator
+// Deprecated: use Generator
+type Config = Generator
+
+// Gen generates the set OpenAPI schema for all top-level types of the
+// given instance.
//
+// Deprecated: use Generator.All.
func Gen(inst *cue.Instance, c *Config) ([]byte, error) {
if c == nil {
c = defaultConfig
}
- comps, err := components(inst, c)
+ all, err := c.All(inst)
if err != nil {
return nil, err
}
- return json.Marshal(comps)
+ return json.Marshal(all)
+}
+
+// All generates an OpenAPI definition from the given instance.
+//
+// Note: only a limited number of top-level types are supported so far.
+func (g *Generator) All(inst *cue.Instance) (OrderedMap, error) {
+ all, err := g.Schemas(inst)
+ if err != nil {
+ return nil, err
+ }
+
+ schemas := &OrderedMap{}
+ schemas.set("schema", all)
+
+ top := OrderedMap{}
+ top.set("openapi", "3.0.0")
+ top.set("components", schemas)
+
+ return top, nil
+}
+
+// Schemas extracts component/schemas from the CUE top-level types.
+func (g *Generator) Schemas(inst *cue.Instance) (OrderedMap, error) {
+ comps, err := schemas(g, inst)
+ if err != nil {
+ return nil, err
+ }
+ return comps, err
}
var defaultConfig = &Config{}
diff --git a/encoding/openapi/orderedmap.go b/encoding/openapi/orderedmap.go
index 74e789f..8d419a8 100644
--- a/encoding/openapi/orderedmap.go
+++ b/encoding/openapi/orderedmap.go
@@ -16,56 +16,64 @@
import "encoding/json"
-type orderedMap []kvPair
+// An OrderedMap is a set of key-value pairs that preserves the order in which
+// items were added. It marshals to JSON as an object.
+type OrderedMap []KeyValue
-type kvPair struct {
- key string
- value interface{}
+// KeyValue associates a value with a key.
+type KeyValue struct {
+ Key string
+ Value interface{}
}
-func (m *orderedMap) Prepend(key string, value interface{}) {
- *m = append([]kvPair{{key, value}}, (*m)...)
+func (m *OrderedMap) prepend(key string, value interface{}) {
+ *m = append([]KeyValue{{key, value}}, (*m)...)
}
-func (m *orderedMap) Set(key string, value interface{}) {
+// set sets a key value pair. If a pair with the same key already existed, it
+// will be replaced with the new value. Otherwise, the new value is added to
+// the end.
+func (m *OrderedMap) set(key string, value interface{}) {
for i, v := range *m {
- if v.key == key {
- (*m)[i].value = value
+ if v.Key == key {
+ (*m)[i].Value = value
return
}
}
- *m = append(*m, kvPair{key, value})
+ *m = append(*m, KeyValue{key, value})
}
-func (m *orderedMap) Exists(key string) bool {
- for _, v := range *m {
- if v.key == key {
+// exists reports whether a key-value pair exists for the given key.
+func (m OrderedMap) exists(key string) bool {
+ for _, v := range m {
+ if v.Key == key {
return true
}
}
return false
}
-func (m *orderedMap) MarshalJSON() (b []byte, err error) {
+// MarshalJSON implements Marshal
+func (m OrderedMap) MarshalJSON() (b []byte, err error) {
b = append(b, '{')
- for i, v := range *m {
+ for i, v := range m {
if i > 0 {
b = append(b, ",\n"...)
}
- key, err := json.Marshal(v.key)
+ key, err := json.Marshal(v.Key)
if je, ok := err.(*json.MarshalerError); ok {
return nil, je.Err
}
b = append(b, key...)
b = append(b, ": "...)
- value, err := json.Marshal(v.value)
+ value, jerr := json.Marshal(v.Value)
if je, ok := err.(*json.MarshalerError); ok {
- // return nil, je.Err
+ err = jerr
value, _ = json.Marshal(je.Err.Error())
}
b = append(b, value...)
}
b = append(b, '}')
- return b, nil
+ return b, err
}
diff --git a/encoding/openapi/types.go b/encoding/openapi/types.go
index 03eada8..5cf481d 100644
--- a/encoding/openapi/types.go
+++ b/encoding/openapi/types.go
@@ -48,7 +48,7 @@
return cueToOpenAPI[string(b)]
}
-func simplify(b *builder, t *orderedMap) {
+func simplify(b *builder, t *OrderedMap) {
if b.format == "" {
return
}
@@ -58,17 +58,17 @@
}
}
-func simplifyNumber(t *orderedMap, format string) string {
+func simplifyNumber(t *OrderedMap, format string) string {
pairs := *t
k := 0
for i, kv := range pairs {
- switch kv.key {
+ switch kv.Key {
case "minimum":
- if decimalEqual(minMap[format], kv.value) {
+ if decimalEqual(minMap[format], kv.Value) {
continue
}
case "maximum":
- if decimalEqual(maxMap[format], kv.value) {
+ if decimalEqual(maxMap[format], kv.Value) {
continue
}
}