Skip to content

Commit cb188d3

Browse files
smyrmanrs
authored andcommitted
Adding MaxLen/MinLen to schema.Array (#59)
This adds MaxLen/MinLen parameters to schema.Array, and updates the jsonschema package to also handle these new parameters. Array tests has been converted to table-tests for both packages. For the schema package, the tests are now Go 1.7 only.
1 parent eb49f3b commit cb188d3

File tree

6 files changed

+200
-30
lines changed

6 files changed

+200
-30
lines changed

schema/all_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// +build go1.7
2+
3+
package schema_test
4+
5+
import (
6+
"testing"
7+
8+
"github.com/rs/rest-layer/schema"
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
type compilerTestCase struct {
13+
Name string
14+
Compiler schema.Compiler
15+
Error string
16+
}
17+
18+
func (tc compilerTestCase) Run(t *testing.T) {
19+
t.Run(tc.Name, func(t *testing.T) {
20+
t.Parallel()
21+
22+
err := tc.Compiler.Compile()
23+
if tc.Error == "" {
24+
assert.NoError(t, err)
25+
} else {
26+
assert.EqualError(t, err, tc.Error)
27+
}
28+
})
29+
}
30+
31+
type fieldValidatorTestCase struct {
32+
Name string
33+
Validator schema.FieldValidator
34+
Input, Expect interface{}
35+
Error string
36+
}
37+
38+
func (tc fieldValidatorTestCase) Run(t *testing.T) {
39+
t.Run(tc.Name, func(t *testing.T) {
40+
t.Parallel()
41+
42+
v, err := tc.Validator.Validate(tc.Input)
43+
if tc.Error == "" {
44+
assert.NoError(t, err)
45+
assert.Equal(t, tc.Expect, v)
46+
} else {
47+
assert.EqualError(t, err, tc.Error)
48+
assert.Nil(t, v)
49+
}
50+
})
51+
}

schema/array.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import (
99
type Array struct {
1010
// ValuesValidator is the validator to apply on array items
1111
ValuesValidator FieldValidator
12+
// MinLen defines the minimum array length (default 0)
13+
MinLen int
14+
// MaxLen defines the maximum array length (default no limit)
15+
MaxLen int
1216
}
1317

1418
// Compile implements Compiler interface
@@ -36,5 +40,12 @@ func (v Array) Validate(value interface{}) (interface{}, error) {
3640
arr[i] = val
3741
}
3842
}
43+
l := len(arr)
44+
if l < v.MinLen {
45+
return nil, fmt.Errorf("has fewer items than %d", v.MinLen)
46+
}
47+
if v.MaxLen > 0 && l > v.MaxLen {
48+
return nil, fmt.Errorf("has more items than %d", v.MaxLen)
49+
}
3950
return arr, nil
4051
}

schema/array_test.go

Lines changed: 71 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,82 @@
1-
package schema
1+
// +build go1.7
2+
3+
package schema_test
24

35
import (
46
"testing"
57

6-
"github.com/stretchr/testify/assert"
8+
"github.com/rs/rest-layer/schema"
79
)
810

911
func TestArrayValidatorCompile(t *testing.T) {
10-
v := &Array{ValuesValidator: &String{}}
11-
err := v.Compile()
12-
assert.NoError(t, err)
13-
v = &Array{ValuesValidator: &String{Regexp: "[invalid re"}}
14-
err = v.Compile()
15-
assert.EqualError(t, err, "invalid regexp: error parsing regexp: missing closing ]: `[invalid re`")
16-
12+
testCases := []compilerTestCase{
13+
{
14+
Name: "ValuesValidator=&String{}",
15+
Compiler: &schema.Array{ValuesValidator: &schema.String{}},
16+
},
17+
{
18+
Name: "ValuesValidator=&String{Regexp:invalid}",
19+
Compiler: &schema.Array{ValuesValidator: &schema.String{Regexp: "[invalid re"}},
20+
Error: "invalid regexp: error parsing regexp: missing closing ]: `[invalid re`",
21+
},
22+
}
23+
for i := range testCases {
24+
testCases[i].Run(t)
25+
}
1726
}
1827

1928
func TestArrayValidator(t *testing.T) {
20-
v, err := Array{ValuesValidator: &Bool{}}.Validate([]interface{}{true, false})
21-
assert.NoError(t, err)
22-
assert.Equal(t, []interface{}{true, false}, v)
23-
v, err = Array{ValuesValidator: &Bool{}}.Validate([]interface{}{true, "value"})
24-
assert.EqualError(t, err, "invalid value at #2: not a Boolean")
25-
assert.Equal(t, nil, v)
26-
v, err = Array{ValuesValidator: &String{}}.Validate("value")
27-
assert.EqualError(t, err, "not an array")
28-
assert.Equal(t, nil, v)
29+
testCases := []fieldValidatorTestCase{
30+
{
31+
Name: `ValuesValidator=nil,Validate([]interface{}{true,"value"})`,
32+
Validator: &schema.Array{},
33+
Input: []interface{}{true, "value"},
34+
Expect: []interface{}{true, "value"},
35+
},
36+
{
37+
Name: `ValuesValidator=&schema.Bool{},Validate([]interface{}{true,false})`,
38+
Validator: &schema.Array{ValuesValidator: &schema.Bool{}},
39+
Input: []interface{}{true, false},
40+
Expect: []interface{}{true, false},
41+
},
42+
{
43+
Name: `ValuesValidator=&schema.Bool{},Validate([]interface{}{true,"value"})`,
44+
Validator: &schema.Array{ValuesValidator: &schema.Bool{}},
45+
Input: []interface{}{true, "value"},
46+
Error: "invalid value at #2: not a Boolean",
47+
},
48+
{
49+
Name: `ValuesValidator=&String{},Validate("value")`,
50+
Validator: &schema.Array{ValuesValidator: &schema.String{}},
51+
Input: "value",
52+
Error: "not an array",
53+
},
54+
{
55+
Name: `MinLen=2,Validate([]interface{}{true,false})`,
56+
Validator: &schema.Array{ValuesValidator: &schema.Bool{}, MinLen: 2},
57+
Input: []interface{}{true, false},
58+
Expect: []interface{}{true, false},
59+
},
60+
{
61+
Name: `MinLen=3,Validate([]interface{}{true,false})`,
62+
Validator: &schema.Array{ValuesValidator: &schema.Bool{}, MinLen: 3},
63+
Input: []interface{}{true, false},
64+
Error: "has fewer items than 3",
65+
},
66+
{
67+
Name: `MaxLen=2,Validate([]interface{}{true,false})`,
68+
Validator: &schema.Array{ValuesValidator: &schema.Bool{}, MaxLen: 2},
69+
Input: []interface{}{true, false},
70+
Expect: []interface{}{true, false},
71+
},
72+
{
73+
Name: `MaxLen=1,Validate([]interface{}{true,false})`,
74+
Validator: &schema.Array{ValuesValidator: &schema.Bool{}, MaxLen: 1},
75+
Input: []interface{}{true, false},
76+
Error: "has more items than 1",
77+
},
78+
}
79+
for i := range testCases {
80+
testCases[i].Run(t)
81+
}
2982
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package jsonschema_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/rs/rest-layer/schema"
7+
)
8+
9+
func TestArray(t *testing.T) {
10+
testCases := []encoderTestCase{
11+
{
12+
name: "ValuesValidator=nil",
13+
schema: schema.Schema{
14+
Fields: schema.Fields{
15+
"a": schema.Field{
16+
Validator: &schema.Array{},
17+
},
18+
},
19+
},
20+
customValidate: fieldValidator("a", `{"type": "array"}`),
21+
},
22+
{
23+
name: "ValuesValidator=&schema.Bool{}",
24+
schema: schema.Schema{
25+
Fields: schema.Fields{
26+
"a": schema.Field{
27+
Validator: &schema.Array{ValuesValidator: &schema.Bool{}},
28+
},
29+
},
30+
},
31+
customValidate: fieldValidator("a", `{"type": "array", "items": {"type": "boolean"}}`),
32+
},
33+
{
34+
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.11
35+
name: "MinLen=42",
36+
schema: schema.Schema{
37+
Fields: schema.Fields{
38+
"a": schema.Field{
39+
Validator: &schema.Array{MinLen: 42},
40+
},
41+
},
42+
},
43+
customValidate: fieldValidator("a", `{"type": "array", "minItems": 42}`),
44+
},
45+
{
46+
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.10
47+
name: "MaxLen=42",
48+
schema: schema.Schema{
49+
Fields: schema.Fields{
50+
"a": schema.Field{
51+
Validator: &schema.Array{MaxLen: 42},
52+
},
53+
},
54+
},
55+
customValidate: fieldValidator("a", `{"type": "array", "maxItems": 42}`),
56+
},
57+
}
58+
for i := range testCases {
59+
testCases[i].Run(t)
60+
}
61+
}

schema/encoding/jsonschema/jsonschema.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,12 @@ func validatorToJSONSchema(w io.Writer, v schema.FieldValidator) (err error) {
144144
}
145145
case *schema.Array:
146146
ew.writeString(`"type": "array"`)
147+
if t.MinLen > 0 {
148+
ew.writeFormat(`, "minItems": %s`, strconv.FormatInt(int64(t.MinLen), 10))
149+
}
150+
if t.MaxLen > 0 {
151+
ew.writeFormat(`, "maxItems": %s`, strconv.FormatInt(int64(t.MaxLen), 10))
152+
}
147153
if t.ValuesValidator != nil {
148154
ew.writeString(`, "items": {`)
149155
if ew.err == nil {

schema/encoding/jsonschema/jsonschema_test.go

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -65,18 +65,6 @@ func TestFloatValidator(t *testing.T) {
6565

6666
}
6767

68-
func TestArray(t *testing.T) {
69-
validator := &schema.Array{}
70-
b := new(bytes.Buffer)
71-
assert.NoError(t, validatorToJSONSchema(b, validator))
72-
_, err := isValidJSON(wrapWithJSONObject(b))
73-
assert.NoError(t, err)
74-
75-
a := assert.New(t)
76-
strJSON := string(wrapWithJSONObject(b))
77-
a.Contains(strJSON, `"type": "array"`)
78-
}
79-
8068
func TestTime(t *testing.T) {
8169
validator := &schema.Time{}
8270
b := new(bytes.Buffer)

0 commit comments

Comments
 (0)