Skip to content

Commit

Permalink
Merge pull request #100 from cloudquery/fix/min-max-ptr
Browse files Browse the repository at this point in the history
fix: Values for section 6.2 should be numbers
  • Loading branch information
samlown committed Sep 26, 2023
2 parents 0101b6e + 372fbaa commit 72e420b
Show file tree
Hide file tree
Showing 17 changed files with 122 additions and 108 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
vendor/
.idea/
6 changes: 6 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ linters-settings:
gocritic:
disabled-checks:
- ifElseChain
gofmt:
rewrite-rules:
- pattern: 'interface{}'
replacement: 'any'
- pattern: 'a[b:len(a)]'
replacement: 'a[b:]'

issues:
max-per-linter: 0
Expand Down
6 changes: 3 additions & 3 deletions examples/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ type User struct {
// Unique sequential identifier.
ID int `json:"id" jsonschema:"required"`
// This comment will be ignored
Name string `json:"name" jsonschema:"required,minLength=1,maxLength=20,pattern=.*,description=this is a property,title=the name,example=joe,example=lucy,default=alex"`
Friends []int `json:"friends,omitempty" jsonschema_description:"list of IDs, omitted when empty"`
Tags map[string]interface{} `json:"tags,omitempty"`
Name string `json:"name" jsonschema:"required,minLength=1,maxLength=20,pattern=.*,description=this is a property,title=the name,example=joe,example=lucy,default=alex"`
Friends []int `json:"friends,omitempty" jsonschema_description:"list of IDs, omitted when empty"`
Tags map[string]any `json:"tags,omitempty"`

// An array of pets the user cares for.
Pets nested.Pets `json:"pets"`
Expand Down
16 changes: 8 additions & 8 deletions examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import (
)

type SampleUser struct {
ID int `json:"id"`
Name string `json:"name" jsonschema:"title=the name,description=The name of a friend,example=joe,example=lucy,default=alex"`
Friends []int `json:"friends,omitempty" jsonschema_description:"The list of IDs, omitted when empty"`
Tags map[string]interface{} `json:"tags,omitempty" jsonschema_extras:"a=b,foo=bar,foo=bar1"`
BirthDate time.Time `json:"birth_date,omitempty" jsonschema:"oneof_required=date"`
YearOfBirth string `json:"year_of_birth,omitempty" jsonschema:"oneof_required=year"`
Metadata interface{} `json:"metadata,omitempty" jsonschema:"oneof_type=string;array"`
FavColor string `json:"fav_color,omitempty" jsonschema:"enum=red,enum=green,enum=blue"`
ID int `json:"id"`
Name string `json:"name" jsonschema:"title=the name,description=The name of a friend,example=joe,example=lucy,default=alex"`
Friends []int `json:"friends,omitempty" jsonschema_description:"The list of IDs, omitted when empty"`
Tags map[string]any `json:"tags,omitempty" jsonschema_extras:"a=b,foo=bar,foo=bar1"`
BirthDate time.Time `json:"birth_date,omitempty" jsonschema:"oneof_required=date"`
YearOfBirth string `json:"year_of_birth,omitempty" jsonschema:"oneof_required=year"`
Metadata any `json:"metadata,omitempty" jsonschema:"oneof_type=string;array"`
FavColor string `json:"fav_color,omitempty" jsonschema:"enum=red,enum=green,enum=blue"`
}

func ExampleReflect() {
Expand Down
4 changes: 2 additions & 2 deletions fixtures/allow_additional_props.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@
"age": {
"type": "integer",
"maximum": 120,
"exclusiveMaximum": true,
"exclusiveMaximum": 121,
"minimum": 18,
"exclusiveMinimum": true
"exclusiveMinimum": 17
},
"email": {
"type": "string",
Expand Down
4 changes: 2 additions & 2 deletions fixtures/custom_base_schema_id.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,9 @@
"age": {
"type": "integer",
"maximum": 120,
"exclusiveMaximum": true,
"exclusiveMaximum": 121,
"minimum": 18,
"exclusiveMinimum": true
"exclusiveMinimum": 17
},
"email": {
"type": "string",
Expand Down
4 changes: 2 additions & 2 deletions fixtures/defaults_expanded_toplevel.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@
"age": {
"type": "integer",
"maximum": 120,
"exclusiveMaximum": true,
"exclusiveMaximum": 121,
"minimum": 18,
"exclusiveMinimum": true
"exclusiveMinimum": 17
},
"email": {
"type": "string",
Expand Down
4 changes: 2 additions & 2 deletions fixtures/ignore_type.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@
"age": {
"type": "integer",
"maximum": 120,
"exclusiveMaximum": true,
"exclusiveMaximum": 121,
"minimum": 18,
"exclusiveMinimum": true
"exclusiveMinimum": 17
},
"email": {
"type": "string",
Expand Down
4 changes: 2 additions & 2 deletions fixtures/no_reference.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,9 @@
"age": {
"type": "integer",
"maximum": 120,
"exclusiveMaximum": true,
"exclusiveMaximum": 121,
"minimum": 18,
"exclusiveMinimum": true
"exclusiveMinimum": 17
},
"email": {
"type": "string",
Expand Down
4 changes: 2 additions & 2 deletions fixtures/no_reference_anchor.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@
"age": {
"type": "integer",
"maximum": 120,
"exclusiveMaximum": true,
"exclusiveMaximum": 121,
"minimum": 18,
"exclusiveMinimum": true
"exclusiveMinimum": 17
},
"email": {
"type": "string",
Expand Down
4 changes: 2 additions & 2 deletions fixtures/required_from_jsontags.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@
"age": {
"type": "integer",
"maximum": 120,
"exclusiveMaximum": true,
"exclusiveMaximum": 121,
"minimum": 18,
"exclusiveMinimum": true
"exclusiveMinimum": 17
},
"email": {
"type": "string",
Expand Down
4 changes: 2 additions & 2 deletions fixtures/test_user.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@
"age": {
"type": "integer",
"maximum": 120,
"exclusiveMaximum": true,
"exclusiveMaximum": 121,
"minimum": 18,
"exclusiveMinimum": true
"exclusiveMinimum": 17
},
"email": {
"type": "string",
Expand Down
4 changes: 2 additions & 2 deletions fixtures/test_user_assign_anchor.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,9 @@
"age": {
"type": "integer",
"maximum": 120,
"exclusiveMaximum": true,
"exclusiveMaximum": 121,
"minimum": 18,
"exclusiveMinimum": true
"exclusiveMinimum": 17
},
"email": {
"type": "string",
Expand Down
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ module github.com/invopop/jsonschema
go 1.18

require (
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0
github.com/stretchr/testify v1.8.1
github.com/wk8/go-ordered-map/v2 v2.1.8
)
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx2
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 h1:i462o439ZjprVSFSZLZxcsoAe592sZB1rci2Z8j4wdk=
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
Expand Down
91 changes: 50 additions & 41 deletions reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,13 @@ type Schema struct {
PropertyNames *Schema `json:"propertyNames,omitempty"` // section 10.3.2.4
// RFC draft-bhutton-json-schema-validation-00, section 6
Type string `json:"type,omitempty"` // section 6.1.1
Enum []interface{} `json:"enum,omitempty"` // section 6.1.2
Const interface{} `json:"const,omitempty"` // section 6.1.3
MultipleOf int `json:"multipleOf,omitempty"` // section 6.2.1
Maximum int `json:"maximum,omitempty"` // section 6.2.2
ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"` // section 6.2.3
Minimum int `json:"minimum,omitempty"` // section 6.2.4
ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty"` // section 6.2.5
Enum []any `json:"enum,omitempty"` // section 6.1.2
Const any `json:"const,omitempty"` // section 6.1.3
MultipleOf json.Number `json:"multipleOf,omitempty"` // section 6.2.1
Maximum json.Number `json:"maximum,omitempty"` // section 6.2.2
ExclusiveMaximum json.Number `json:"exclusiveMaximum,omitempty"` // section 6.2.3
Minimum json.Number `json:"minimum,omitempty"` // section 6.2.4
ExclusiveMinimum json.Number `json:"exclusiveMinimum,omitempty"` // section 6.2.5
MaxLength int `json:"maxLength,omitempty"` // section 6.3.1
MinLength int `json:"minLength,omitempty"` // section 6.3.2
Pattern string `json:"pattern,omitempty"` // section 6.3.3
Expand All @@ -80,15 +80,15 @@ type Schema struct {
ContentMediaType string `json:"contentMediaType,omitempty"` // section 8.4
ContentSchema *Schema `json:"contentSchema,omitempty"` // section 8.5
// RFC draft-bhutton-json-schema-validation-00, section 9
Title string `json:"title,omitempty"` // section 9.1
Description string `json:"description,omitempty"` // section 9.1
Default interface{} `json:"default,omitempty"` // section 9.2
Deprecated bool `json:"deprecated,omitempty"` // section 9.3
ReadOnly bool `json:"readOnly,omitempty"` // section 9.4
WriteOnly bool `json:"writeOnly,omitempty"` // section 9.4
Examples []interface{} `json:"examples,omitempty"` // section 9.5
Title string `json:"title,omitempty"` // section 9.1
Description string `json:"description,omitempty"` // section 9.1
Default any `json:"default,omitempty"` // section 9.2
Deprecated bool `json:"deprecated,omitempty"` // section 9.3
ReadOnly bool `json:"readOnly,omitempty"` // section 9.4
WriteOnly bool `json:"writeOnly,omitempty"` // section 9.4
Examples []any `json:"examples,omitempty"` // section 9.5

Extras map[string]interface{} `json:"-"`
Extras map[string]any `json:"-"`

// Special boolean representation of the Schema - section 4.3.2
boolean *bool
Expand Down Expand Up @@ -127,7 +127,7 @@ type customGetFieldDocString func(fieldName string) string
var customStructGetFieldDocString = reflect.TypeOf((*customSchemaGetFieldDocString)(nil)).Elem()

// Reflect reflects to Schema from a value using the default Reflector
func Reflect(v interface{}) *Schema {
func Reflect(v any) *Schema {
return ReflectFromType(reflect.TypeOf(v))
}

Expand Down Expand Up @@ -188,7 +188,7 @@ type Reflector struct {

// IgnoredTypes defines a slice of types that should be ignored in the schema,
// switching to just allowing additional properties instead.
IgnoredTypes []interface{}
IgnoredTypes []any

// Lookup allows a function to be defined that will provide a custom mapping of
// types to Schema IDs. This allows existing schema documents to be referenced
Expand Down Expand Up @@ -229,7 +229,7 @@ type Reflector struct {
}

// Reflect reflects to Schema from a value.
func (r *Reflector) Reflect(v interface{}) *Schema {
func (r *Reflector) Reflect(v any) *Schema {
return r.ReflectFromType(reflect.TypeOf(v))
}

Expand Down Expand Up @@ -676,7 +676,7 @@ func (t *Schema) structKeywordsFromTags(f reflect.StructField, parent *Schema, p
t.extraKeywords(extras)
}

// read struct tags for generic keyworks
// read struct tags for generic keywords
func (t *Schema) genericKeywords(tags []string, parent *Schema, propertyName string) { //nolint:gocyclo
for _, tag := range tags {
nameValue := strings.Split(tag, "=")
Expand Down Expand Up @@ -789,7 +789,7 @@ func (t *Schema) genericKeywords(tags []string, parent *Schema, propertyName str
}
}

// read struct tags for boolean type keyworks
// read struct tags for boolean type keywords
func (t *Schema) booleanKeywords(tags []string) {
for _, tag := range tags {
nameValue := strings.Split(tag, "=")
Expand All @@ -807,7 +807,7 @@ func (t *Schema) booleanKeywords(tags []string) {
}
}

// read struct tags for string type keyworks
// read struct tags for string type keywords
func (t *Schema) stringKeywords(tags []string) {
for _, tag := range tags {
nameValue := strings.Split(tag, "=")
Expand Down Expand Up @@ -842,41 +842,37 @@ func (t *Schema) stringKeywords(tags []string) {
}
}

// read struct tags for numerical type keyworks
// read struct tags for numerical type keywords
func (t *Schema) numericalKeywords(tags []string) {
for _, tag := range tags {
nameValue := strings.Split(tag, "=")
if len(nameValue) == 2 {
name, val := nameValue[0], nameValue[1]
switch name {
case "multipleOf":
i, _ := strconv.Atoi(val)
t.MultipleOf = i
t.MultipleOf, _ = toJSONNumber(val)
case "minimum":
i, _ := strconv.Atoi(val)
t.Minimum = i
t.Minimum, _ = toJSONNumber(val)
case "maximum":
i, _ := strconv.Atoi(val)
t.Maximum = i
t.Maximum, _ = toJSONNumber(val)
case "exclusiveMaximum":
b, _ := strconv.ParseBool(val)
t.ExclusiveMaximum = b
t.ExclusiveMaximum, _ = toJSONNumber(val)
case "exclusiveMinimum":
b, _ := strconv.ParseBool(val)
t.ExclusiveMinimum = b
t.ExclusiveMinimum, _ = toJSONNumber(val)
case "default":
n, _ := strconv.ParseFloat(val, 64)
t.Default = n
if num, ok := toJSONNumber(val); ok {
t.Default = num
}
case "example":
if i, err := strconv.Atoi(val); err == nil {
t.Examples = append(t.Examples, i)
if num, ok := toJSONNumber(val); ok {
t.Examples = append(t.Examples, num)
}
}
}
}
}

// read struct tags for object type keyworks
// read struct tags for object type keywords
// func (t *Type) objectKeywords(tags []string) {
// for _, tag := range tags{
// nameValue := strings.Split(tag, "=")
Expand All @@ -892,9 +888,9 @@ func (t *Schema) numericalKeywords(tags []string) {
// }
// }

// read struct tags for array type keyworks
// read struct tags for array type keywords
func (t *Schema) arrayKeywords(tags []string) {
var defaultValues []interface{}
var defaultValues []any
for _, tag := range tags {
nameValue := strings.Split(tag, "=")
if len(nameValue) == 2 {
Expand Down Expand Up @@ -944,7 +940,7 @@ func (t *Schema) extraKeywords(tags []string) {

func (t *Schema) setExtra(key, val string) {
if t.Extras == nil {
t.Extras = map[string]interface{}{}
t.Extras = map[string]any{}
}
if existingVal, ok := t.Extras[key]; ok {
switch existingVal := existingVal.(type) {
Expand All @@ -962,7 +958,7 @@ func (t *Schema) setExtra(key, val string) {
case "minimum":
t.Extras[key], _ = strconv.Atoi(val)
default:
var x interface{}
var x any
if val == "true" {
x = true
} else if val == "false" {
Expand Down Expand Up @@ -1020,6 +1016,19 @@ func ignoredByJSONSchemaTags(tags []string) bool {
return tags[0] == "-"
}

// toJSONNumber converts string to *json.Number.
// It'll aso return whether the number is valid.
func toJSONNumber(s string) (json.Number, bool) {
num := json.Number(s)
if _, err := num.Int64(); err == nil {
return num, true
}
if _, err := num.Float64(); err == nil {
return num, true
}
return json.Number(""), false
}

func (r *Reflector) fieldNameTag() string {
if r.FieldNameTag != "" {
return r.FieldNameTag
Expand Down

0 comments on commit 72e420b

Please sign in to comment.