encoding/openapi|jsonschema: allow bool for exclusiveNum
At least for v3.0.0 this is expected. Only
as of v3.1.0 will OpenAPI adopt the JSON schema
semantics.
Fixes #412
Change-Id: Ibb43ef4794ec6500e27392d981cda655ecec0517
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6361
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/testdata/script/def_openapi.txt b/cmd/cue/cmd/testdata/script/def_openapi.txt
index 3e05937..3ea4fbc 100644
--- a/cmd/cue/cmd/testdata/script/def_openapi.txt
+++ b/cmd/cue/cmd/testdata/script/def_openapi.txt
@@ -143,7 +143,8 @@
"b": {
"type": "integer",
"minimum": 0,
- "exclusiveMaximum": 10
+ "maximum": 10,
+ "exclusiveMaximum": true
}
}
}
@@ -176,7 +177,8 @@
b:
type: integer
minimum: 0
- exclusiveMaximum: 10
+ maximum: 10
+ exclusiveMaximum: true
-- expect-cue-out --
openapi: "3.0.0"
info: {
@@ -198,7 +200,8 @@
b: {
type: "integer"
minimum: 0
- exclusiveMaximum: 10
+ maximum: 10
+ exclusiveMaximum: true
}
}
}
diff --git a/cmd/cue/cmd/testdata/script/import_auto.txt b/cmd/cue/cmd/testdata/script/import_auto.txt
index 785d242..4c555f2 100644
--- a/cmd/cue/cmd/testdata/script/import_auto.txt
+++ b/cmd/cue/cmd/testdata/script/import_auto.txt
@@ -13,7 +13,7 @@
#Foo: {
a: int
- b: int & >=0 & <10
+ b: int & <10 & >=0
...
}
#Bar: {
diff --git a/encoding/jsonschema/constraints.go b/encoding/jsonschema/constraints.go
index bc45cdf..ef941e2 100644
--- a/encoding/jsonschema/constraints.go
+++ b/encoding/jsonschema/constraints.go
@@ -403,24 +403,38 @@
// Number constraints
- p1("minimum", func(n cue.Value, s *state) {
+ p2("minimum", func(n cue.Value, s *state) {
s.usedTypes |= cue.NumberKind
- s.add(n, numType, &ast.UnaryExpr{Op: token.GEQ, X: s.number(n)})
+ op := token.GEQ
+ if s.exclusiveMin {
+ op = token.GTR
+ }
+ s.add(n, numType, &ast.UnaryExpr{Op: op, X: s.number(n)})
}),
p1("exclusiveMinimum", func(n cue.Value, s *state) {
- // TODO: should we support Draft 4 booleans?
+ if n.Kind() == cue.BoolKind {
+ s.exclusiveMin = true
+ return
+ }
s.usedTypes |= cue.NumberKind
s.add(n, numType, &ast.UnaryExpr{Op: token.GTR, X: s.number(n)})
}),
- p1("maximum", func(n cue.Value, s *state) {
+ p2("maximum", func(n cue.Value, s *state) {
s.usedTypes |= cue.NumberKind
- s.add(n, numType, &ast.UnaryExpr{Op: token.LEQ, X: s.number(n)})
+ op := token.LEQ
+ if s.exclusiveMax {
+ op = token.LSS
+ }
+ s.add(n, numType, &ast.UnaryExpr{Op: op, X: s.number(n)})
}),
p1("exclusiveMaximum", func(n cue.Value, s *state) {
- // TODO: should we support Draft 4 booleans?
+ if n.Kind() == cue.BoolKind {
+ s.exclusiveMax = true
+ return
+ }
s.usedTypes |= cue.NumberKind
s.add(n, numType, &ast.UnaryExpr{Op: token.LSS, X: s.number(n)})
}),
diff --git a/encoding/jsonschema/decode.go b/encoding/jsonschema/decode.go
index de83b8c..30b0dc7 100644
--- a/encoding/jsonschema/decode.go
+++ b/encoding/jsonschema/decode.go
@@ -327,13 +327,15 @@
usedTypes cue.Kind
allowedTypes cue.Kind
- default_ ast.Expr
- examples []ast.Expr
- title string
- description string
- deprecated bool
- jsonschema string
- id *url.URL // base URI for $ref
+ default_ ast.Expr
+ examples []ast.Expr
+ title string
+ description string
+ deprecated bool
+ exclusiveMin bool // For OpenAPI and legacy support.
+ exclusiveMax bool // For OpenAPI and legacy support.
+ jsonschema string
+ id *url.URL // base URI for $ref
definitions []ast.Decl
diff --git a/encoding/jsonschema/testdata/num.txtar b/encoding/jsonschema/testdata/num.txtar
index cbbd984..0b53a7a 100644
--- a/encoding/jsonschema/testdata/num.txtar
+++ b/encoding/jsonschema/testdata/num.txtar
@@ -23,6 +23,13 @@
"maximum": 3,
"maxLength": 5
},
+ "legacy": {
+ "type": "number",
+ "exclusiveMinimum": true,
+ "minimum": 2,
+ "exclusiveMaximum": true,
+ "maximum": 3
+ },
"cents": {
"type": "number",
"multipleOf": 0.05
@@ -42,4 +49,5 @@
inclusive?: >=2 & <=3
exclusive?: int & >2 & <3
multi?: int & >=2 & <=3 | strings.MaxRunes(5)
+legacy?: >2 & <3
cents?: math.MultipleOf(0.05)
diff --git a/encoding/openapi/build.go b/encoding/openapi/build.go
index e3f5d17..adb9669 100644
--- a/encoding/openapi/build.go
+++ b/encoding/openapi/build.go
@@ -37,12 +37,13 @@
refPrefix string
path []string
- expandRefs bool
- structural bool
- nameFunc func(inst *cue.Instance, path []string) string
- descFunc func(v cue.Value) string
- fieldFilter *regexp.Regexp
- evalDepth int // detect cycles when resolving references
+ expandRefs bool
+ structural bool
+ exclusiveBool bool
+ nameFunc func(inst *cue.Instance, path []string) string
+ descFunc func(v cue.Value) string
+ fieldFilter *regexp.Regexp
+ evalDepth int // detect cycles when resolving references
schemas *OrderedMap
@@ -79,6 +80,10 @@
}
}
+ if g.Version == "" {
+ g.Version = "3.0.0"
+ }
+
c := buildContext{
inst: inst,
instExt: inst,
@@ -92,6 +97,14 @@
fieldFilter: fieldFilter,
}
+ switch g.Version {
+ case "3.0.0":
+ c.exclusiveBool = true
+ case "3.1.0":
+ default:
+ return nil, errors.Newf(token.NoPos, "unsupported version %s", g.Version)
+ }
+
defer func() {
switch x := recover().(type) {
case nil:
@@ -888,13 +901,23 @@
switch op, a := v.Expr(); op {
case cue.LessThanOp:
- b.setFilter("Schema", "exclusiveMaximum", b.big(a[0]))
+ if b.ctx.exclusiveBool {
+ b.setFilter("Schema", "exclusiveMaximum", ast.NewBool(true))
+ b.setFilter("Schema", "maximum", b.big(a[0]))
+ } else {
+ b.setFilter("Schema", "exclusiveMaximum", b.big(a[0]))
+ }
case cue.LessThanEqualOp:
b.setFilter("Schema", "maximum", b.big(a[0]))
case cue.GreaterThanOp:
- b.setFilter("Schema", "exclusiveMinimum", b.big(a[0]))
+ if b.ctx.exclusiveBool {
+ b.setFilter("Schema", "exclusiveMinimum", ast.NewBool(true))
+ b.setFilter("Schema", "minimum", b.big(a[0]))
+ } else {
+ b.setFilter("Schema", "exclusiveMinimum", b.big(a[0]))
+ }
case cue.GreaterThanEqualOp:
b.setFilter("Schema", "minimum", b.big(a[0]))
diff --git a/encoding/openapi/openapi.go b/encoding/openapi/openapi.go
index 9613331..f26cff6 100644
--- a/encoding/openapi/openapi.go
+++ b/encoding/openapi/openapi.go
@@ -51,6 +51,9 @@
// in this document.
SelfContained bool
+ // OpenAPI version to use. Supported as of v3.0.0.
+ Version string
+
// FieldFilter defines a regular expression of all fields to omit from the
// output. It is only allowed to filter fields that add additional
// constraints. Fields that indicate basic types cannot be removed. It is
@@ -203,7 +206,7 @@
}
return ast.NewStruct(
- "openapi", ast.NewString("3.0.0"),
+ "openapi", ast.NewString(c.Version),
"info", info,
"paths", ast.NewStruct(),
"components", ast.NewStruct("schemas", schemas),
diff --git a/encoding/openapi/openapi_test.go b/encoding/openapi/openapi_test.go
index d2f32e6..e8c3705 100644
--- a/encoding/openapi/openapi_test.go
+++ b/encoding/openapi/openapi_test.go
@@ -78,6 +78,10 @@
"nums.json",
defaultConfig,
}, {
+ "nums.cue",
+ "nums-v3.1.0.json",
+ &openapi.Config{Info: info, Version: "3.1.0"},
+ }, {
"builtins.cue",
"builtins.json",
defaultConfig,
diff --git a/encoding/openapi/testdata/issue131.json b/encoding/openapi/testdata/issue131.json
index 7257f5f..3025bf5 100644
--- a/encoding/openapi/testdata/issue131.json
+++ b/encoding/openapi/testdata/issue131.json
@@ -16,11 +16,13 @@
"properties": {
"a": {
"type": "number",
- "exclusiveMinimum": 50
+ "minimum": 50,
+ "exclusiveMinimum": true
},
"b": {
"type": "number",
- "exclusiveMaximum": 10
+ "maximum": 10,
+ "exclusiveMaximum": true
}
}
},
diff --git a/encoding/openapi/testdata/nums-v3.1.0.json b/encoding/openapi/testdata/nums-v3.1.0.json
new file mode 100644
index 0000000..4c3cc67
--- /dev/null
+++ b/encoding/openapi/testdata/nums-v3.1.0.json
@@ -0,0 +1,37 @@
+{
+ "openapi": "3.1.0",
+ "info": {
+ "title": "test",
+ "version": "v1"
+ },
+ "paths": {},
+ "components": {
+ "schemas": {
+ "exMax": {
+ "type": "number",
+ "exclusiveMaximum": 6
+ },
+ "exMin": {
+ "type": "number",
+ "exclusiveMinimum": 5
+ },
+ "mul": {
+ "type": "number",
+ "multipleOf": 5
+ },
+ "neq": {
+ "type": "number",
+ "not": {
+ "allOff": [
+ {
+ "minimum": 4
+ },
+ {
+ "maximum": 4
+ }
+ ]
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/encoding/openapi/testdata/nums.cue b/encoding/openapi/testdata/nums.cue
index c765945..94d178b 100644
--- a/encoding/openapi/testdata/nums.cue
+++ b/encoding/openapi/testdata/nums.cue
@@ -3,3 +3,6 @@
#mul: math.MultipleOf(5)
#neq: !=4
+
+#exMin: >5
+#exMax: <6
diff --git a/encoding/openapi/testdata/nums.json b/encoding/openapi/testdata/nums.json
index c032f20..18332db 100644
--- a/encoding/openapi/testdata/nums.json
+++ b/encoding/openapi/testdata/nums.json
@@ -7,6 +7,16 @@
"paths": {},
"components": {
"schemas": {
+ "exMax": {
+ "type": "number",
+ "maximum": 6,
+ "exclusiveMaximum": true
+ },
+ "exMin": {
+ "type": "number",
+ "minimum": 5,
+ "exclusiveMinimum": true
+ },
"mul": {
"type": "number",
"multipleOf": 5
diff --git a/encoding/openapi/testdata/openapi-norefs.json b/encoding/openapi/testdata/openapi-norefs.json
index 2839706..f5c4dfd 100644
--- a/encoding/openapi/testdata/openapi-norefs.json
+++ b/encoding/openapi/testdata/openapi-norefs.json
@@ -110,8 +110,10 @@
},
"foo": {
"type": "number",
- "exclusiveMinimum": 10,
- "exclusiveMaximum": 1000
+ "minimum": 10,
+ "exclusiveMinimum": true,
+ "maximum": 1000,
+ "exclusiveMaximum": true
},
"bar": {
"type": "array",
diff --git a/encoding/openapi/testdata/openapi.json b/encoding/openapi/testdata/openapi.json
index b34244d..90bd1f5 100644
--- a/encoding/openapi/testdata/openapi.json
+++ b/encoding/openapi/testdata/openapi.json
@@ -103,8 +103,10 @@
"$ref": "#/components/schemas/Int32"
},
{
- "exclusiveMinimum": 10,
- "exclusiveMaximum": 1000
+ "exclusiveMinimum": true,
+ "minimum": 10,
+ "exclusiveMaximum": true,
+ "maximum": 1000
}
]
},