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

Commit

Permalink
encoding/openapi|jsonschema: allow bool for exclusiveNum
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
mpvl committed Jun 16, 2020
1 parent 50b9067 commit 2a8b4ed
Show file tree
Hide file tree
Showing 14 changed files with 145 additions and 32 deletions.
9 changes: 6 additions & 3 deletions cmd/cue/cmd/testdata/script/def_openapi.txt
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ $version: "v1"
"b": {
"type": "integer",
"minimum": 0,
"exclusiveMaximum": 10
"maximum": 10,
"exclusiveMaximum": true
}
}
}
Expand Down Expand Up @@ -176,7 +177,8 @@ components:
b:
type: integer
minimum: 0
exclusiveMaximum: 10
maximum: 10
exclusiveMaximum: true
-- expect-cue-out --
openapi: "3.0.0"
info: {
Expand All @@ -198,7 +200,8 @@ components: schemas: {
b: {
type: "integer"
minimum: 0
exclusiveMaximum: 10
maximum: 10
exclusiveMaximum: true
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/cue/cmd/testdata/script/import_auto.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ info: {

#Foo: {
a: int
b: int & >=0 & <10
b: int & <10 & >=0
...
}
#Bar: {
Expand Down
26 changes: 20 additions & 6 deletions encoding/jsonschema/constraints.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,24 +403,38 @@ var constraints = []*constraint{

// 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)})
}),
Expand Down
16 changes: 9 additions & 7 deletions encoding/jsonschema/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,13 +327,15 @@ type state struct {
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

Expand Down
8 changes: 8 additions & 0 deletions encoding/jsonschema/testdata/num.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -42,4 +49,5 @@ several?: 1 | 2 | 3 | 4
inclusive?: >=2 & <=3
exclusive?: int & >2 & <3
multi?: int & >=2 & <=3 | strings.MaxRunes(5)
legacy?: >2 & <3
cents?: math.MultipleOf(0.05)
39 changes: 31 additions & 8 deletions encoding/openapi/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,13 @@ type buildContext struct {
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

Expand Down Expand Up @@ -79,6 +80,10 @@ func schemas(g *Generator, inst *cue.Instance) (schemas *ast.StructLit, err erro
}
}

if g.Version == "" {
g.Version = "3.0.0"
}

c := buildContext{
inst: inst,
instExt: inst,
Expand All @@ -92,6 +97,14 @@ func schemas(g *Generator, inst *cue.Instance) (schemas *ast.StructLit, err erro
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:
Expand Down Expand Up @@ -888,13 +901,23 @@ func (b *builder) number(v cue.Value) {

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]))
Expand Down
5 changes: 4 additions & 1 deletion encoding/openapi/openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ type Config struct {
// 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
Expand Down Expand Up @@ -203,7 +206,7 @@ func (c *Config) compose(inst *cue.Instance, schemas *ast.StructLit) (x *ast.Str
}

return ast.NewStruct(
"openapi", ast.NewString("3.0.0"),
"openapi", ast.NewString(c.Version),
"info", info,
"paths", ast.NewStruct(),
"components", ast.NewStruct("schemas", schemas),
Expand Down
4 changes: 4 additions & 0 deletions encoding/openapi/openapi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ func TestParseDefinitions(t *testing.T) {
"nums.cue",
"nums.json",
defaultConfig,
}, {
"nums.cue",
"nums-v3.1.0.json",
&openapi.Config{Info: info, Version: "3.1.0"},
}, {
"builtins.cue",
"builtins.json",
Expand Down
6 changes: 4 additions & 2 deletions encoding/openapi/testdata/issue131.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@
"properties": {
"a": {
"type": "number",
"exclusiveMinimum": 50
"minimum": 50,
"exclusiveMinimum": true
},
"b": {
"type": "number",
"exclusiveMaximum": 10
"maximum": 10,
"exclusiveMaximum": true
}
}
},
Expand Down
37 changes: 37 additions & 0 deletions encoding/openapi/testdata/nums-v3.1.0.json
Original file line number Diff line number Diff line change
@@ -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
}
]
}
}
}
}
}
3 changes: 3 additions & 0 deletions encoding/openapi/testdata/nums.cue
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ import "math"
#mul: math.MultipleOf(5)

#neq: !=4

#exMin: >5
#exMax: <6
10 changes: 10 additions & 0 deletions encoding/openapi/testdata/nums.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions encoding/openapi/testdata/openapi-norefs.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,10 @@
},
"foo": {
"type": "number",
"exclusiveMinimum": 10,
"exclusiveMaximum": 1000
"minimum": 10,
"exclusiveMinimum": true,
"maximum": 1000,
"exclusiveMaximum": true
},
"bar": {
"type": "array",
Expand Down
6 changes: 4 additions & 2 deletions encoding/openapi/testdata/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,10 @@
"$ref": "#/components/schemas/Int32"
},
{
"exclusiveMinimum": 10,
"exclusiveMaximum": 1000
"exclusiveMinimum": true,
"minimum": 10,
"exclusiveMaximum": true,
"maximum": 1000
}
]
},
Expand Down

0 comments on commit 2a8b4ed

Please sign in to comment.