Skip to content

Commit a490b92

Browse files
authored
added helpers to manipulate validation in schemas and simple schemas (#132)
This only adds things and do not alter the exposed interface or types. Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
1 parent 4d36578 commit a490b92

File tree

10 files changed

+434
-16
lines changed

10 files changed

+434
-16
lines changed

header.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,12 @@ func (h *Header) AllowDuplicates() *Header {
141141
return h
142142
}
143143

144+
// WithValidations is a fluent method to set header validations
145+
func (h *Header) WithValidations(val CommonValidations) *Header {
146+
h.SetValidations(SchemaValidations{CommonValidations: val})
147+
return h
148+
}
149+
144150
// MarshalJSON marshal this to JSON
145151
func (h Header) MarshalJSON() ([]byte, error) {
146152
b1, err := json.Marshal(h.CommonValidations)

header_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"encoding/json"
1919
"testing"
2020

21+
"github.com/go-openapi/swag"
2122
"github.com/stretchr/testify/assert"
2223
)
2324

@@ -166,3 +167,8 @@ func TestWithHeader(t *testing.T) {
166167
},
167168
}, *h)
168169
}
170+
171+
func TestHeaderWithValidation(t *testing.T) {
172+
h := new(Header).WithValidations(CommonValidations{MaxLength: swag.Int64(15)})
173+
assert.EqualValues(t, swag.Int64(15), h.MaxLength)
174+
}

items.go

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -53,22 +53,6 @@ func (s *SimpleSchema) ItemsTypeName() string {
5353
return s.Items.TypeName()
5454
}
5555

56-
// CommonValidations describe common JSON-schema validations
57-
type CommonValidations struct {
58-
Maximum *float64 `json:"maximum,omitempty"`
59-
ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"`
60-
Minimum *float64 `json:"minimum,omitempty"`
61-
ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty"`
62-
MaxLength *int64 `json:"maxLength,omitempty"`
63-
MinLength *int64 `json:"minLength,omitempty"`
64-
Pattern string `json:"pattern,omitempty"`
65-
MaxItems *int64 `json:"maxItems,omitempty"`
66-
MinItems *int64 `json:"minItems,omitempty"`
67-
UniqueItems bool `json:"uniqueItems,omitempty"`
68-
MultipleOf *float64 `json:"multipleOf,omitempty"`
69-
Enum []interface{} `json:"enum,omitempty"`
70-
}
71-
7256
// Items a limited subset of JSON-Schema's items object.
7357
// It is used by parameter definitions that are not located in "body".
7458
//
@@ -180,6 +164,12 @@ func (i *Items) AllowDuplicates() *Items {
180164
return i
181165
}
182166

167+
// WithValidations is a fluent method to set Items validations
168+
func (i *Items) WithValidations(val CommonValidations) *Items {
169+
i.SetValidations(SchemaValidations{CommonValidations: val})
170+
return i
171+
}
172+
183173
// UnmarshalJSON hydrates this items instance with the data from JSON
184174
func (i *Items) UnmarshalJSON(data []byte) error {
185175
var validations CommonValidations

items_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,8 @@ func TestJSONLookupItems(t *testing.T) {
185185
return
186186
}
187187
}
188+
189+
func TestItemsWithValidation(t *testing.T) {
190+
i := new(Items).WithValidations(CommonValidations{MaxLength: swag.Int64(15)})
191+
assert.EqualValues(t, swag.Int64(15), i.MaxLength)
192+
}

parameter.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,12 @@ func (p *Parameter) AllowDuplicates() *Parameter {
277277
return p
278278
}
279279

280+
// WithValidations is a fluent method to set parameter validations
281+
func (p *Parameter) WithValidations(val CommonValidations) *Parameter {
282+
p.SetValidations(SchemaValidations{CommonValidations: val})
283+
return p
284+
}
285+
280286
// UnmarshalJSON hydrates this items instance with the data from JSON
281287
func (p *Parameter) UnmarshalJSON(data []byte) error {
282288
if err := json.Unmarshal(data, &p.CommonValidations); err != nil {

parameters_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"encoding/json"
1919
"testing"
2020

21+
"github.com/go-openapi/swag"
2122
"github.com/stretchr/testify/assert"
2223
)
2324

@@ -162,3 +163,8 @@ func TestParameterGobEncoding(t *testing.T) {
162163
}
163164
doTestAnyGobEncoding(t, &src, &dst)
164165
}
166+
167+
func TestParametersWithValidation(t *testing.T) {
168+
p := new(Parameter).WithValidations(CommonValidations{MaxLength: swag.Int64(15)})
169+
assert.EqualValues(t, swag.Int64(15), p.MaxLength)
170+
}

schema.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,56 @@ func (s *Schema) AsUnwrappedXML() *Schema {
513513
return s
514514
}
515515

516+
// SetValidations defines all schema validations.
517+
//
518+
// NOTE: Required, ReadOnly, AllOf, AnyOf, OneOf and Not are not considered.
519+
func (s *Schema) SetValidations(val SchemaValidations) {
520+
s.Maximum = val.Maximum
521+
s.ExclusiveMaximum = val.ExclusiveMaximum
522+
s.Minimum = val.Minimum
523+
s.ExclusiveMinimum = val.ExclusiveMinimum
524+
s.MaxLength = val.MaxLength
525+
s.MinLength = val.MinLength
526+
s.Pattern = val.Pattern
527+
s.MaxItems = val.MaxItems
528+
s.MinItems = val.MinItems
529+
s.UniqueItems = val.UniqueItems
530+
s.MultipleOf = val.MultipleOf
531+
s.Enum = val.Enum
532+
s.MinProperties = val.MinProperties
533+
s.MaxProperties = val.MaxProperties
534+
s.PatternProperties = val.PatternProperties
535+
}
536+
537+
// WithValidations is a fluent method to set schema validations
538+
func (s *Schema) WithValidations(val SchemaValidations) *Schema {
539+
s.SetValidations(val)
540+
return s
541+
}
542+
543+
// Validations returns a clone of the validations for this schema
544+
func (s Schema) Validations() SchemaValidations {
545+
return SchemaValidations{
546+
CommonValidations: CommonValidations{
547+
Maximum: s.Maximum,
548+
ExclusiveMaximum: s.ExclusiveMaximum,
549+
Minimum: s.Minimum,
550+
ExclusiveMinimum: s.ExclusiveMinimum,
551+
MaxLength: s.MaxLength,
552+
MinLength: s.MinLength,
553+
Pattern: s.Pattern,
554+
MaxItems: s.MaxItems,
555+
MinItems: s.MinItems,
556+
UniqueItems: s.UniqueItems,
557+
MultipleOf: s.MultipleOf,
558+
Enum: s.Enum,
559+
},
560+
MinProperties: s.MinProperties,
561+
MaxProperties: s.MaxProperties,
562+
PatternProperties: s.PatternProperties,
563+
}
564+
}
565+
516566
// MarshalJSON marshal this to JSON
517567
func (s Schema) MarshalJSON() ([]byte, error) {
518568
b1, err := json.Marshal(s.SchemaProps)

schema_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"encoding/json"
1919
"testing"
2020

21+
"github.com/go-openapi/swag"
2122
"github.com/stretchr/testify/assert"
2223
)
2324

@@ -210,3 +211,13 @@ func BenchmarkSchemaUnmarshal(b *testing.B) {
210211
_ = sch.UnmarshalJSON([]byte(schemaJSON))
211212
}
212213
}
214+
215+
func TestSchemaWithValidation(t *testing.T) {
216+
s := new(Schema).WithValidations(SchemaValidations{CommonValidations: CommonValidations{MaxLength: swag.Int64(15)}})
217+
assert.EqualValues(t, swag.Int64(15), s.MaxLength)
218+
219+
val := mkVal()
220+
s = new(Schema).WithValidations(val)
221+
222+
assert.EqualValues(t, val, s.Validations())
223+
}

validations.go

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
package spec
2+
3+
// CommonValidations describe common JSON-schema validations
4+
type CommonValidations struct {
5+
Maximum *float64 `json:"maximum,omitempty"`
6+
ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"`
7+
Minimum *float64 `json:"minimum,omitempty"`
8+
ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty"`
9+
MaxLength *int64 `json:"maxLength,omitempty"`
10+
MinLength *int64 `json:"minLength,omitempty"`
11+
Pattern string `json:"pattern,omitempty"`
12+
MaxItems *int64 `json:"maxItems,omitempty"`
13+
MinItems *int64 `json:"minItems,omitempty"`
14+
UniqueItems bool `json:"uniqueItems,omitempty"`
15+
MultipleOf *float64 `json:"multipleOf,omitempty"`
16+
Enum []interface{} `json:"enum,omitempty"`
17+
}
18+
19+
// SetValidations defines all validations for a simple schema.
20+
//
21+
// NOTE: the input is the larger set of validations available for schemas.
22+
// For simple schemas, MinProperties and MaxProperties are ignored.
23+
func (v *CommonValidations) SetValidations(val SchemaValidations) {
24+
v.Maximum = val.Maximum
25+
v.ExclusiveMaximum = val.ExclusiveMaximum
26+
v.Minimum = val.Minimum
27+
v.ExclusiveMinimum = val.ExclusiveMinimum
28+
v.MaxLength = val.MaxLength
29+
v.MinLength = val.MinLength
30+
v.Pattern = val.Pattern
31+
v.MaxItems = val.MaxItems
32+
v.MinItems = val.MinItems
33+
v.UniqueItems = val.UniqueItems
34+
v.MultipleOf = val.MultipleOf
35+
v.Enum = val.Enum
36+
}
37+
38+
type clearedValidation struct {
39+
Validation string
40+
Value interface{}
41+
}
42+
43+
type clearedValidations []clearedValidation
44+
45+
func (c clearedValidations) apply(cbs []func(string, interface{})) {
46+
for _, cb := range cbs {
47+
for _, cleared := range c {
48+
cb(cleared.Validation, cleared.Value)
49+
}
50+
}
51+
}
52+
53+
// ClearNumberValidations clears all number validations.
54+
//
55+
// Some callbacks may be set by the caller to capture changed values.
56+
func (v *CommonValidations) ClearNumberValidations(cbs ...func(string, interface{})) {
57+
done := make(clearedValidations, 0, 5)
58+
defer func() {
59+
done.apply(cbs)
60+
}()
61+
62+
if v.Minimum != nil {
63+
done = append(done, clearedValidation{Validation: "minimum", Value: v.Minimum})
64+
v.Minimum = nil
65+
}
66+
if v.Maximum != nil {
67+
done = append(done, clearedValidation{Validation: "maximum", Value: v.Maximum})
68+
v.Maximum = nil
69+
}
70+
if v.ExclusiveMaximum {
71+
done = append(done, clearedValidation{Validation: "exclusiveMaximum", Value: v.ExclusiveMaximum})
72+
v.ExclusiveMaximum = false
73+
}
74+
if v.ExclusiveMinimum {
75+
done = append(done, clearedValidation{Validation: "exclusiveMinimum", Value: v.ExclusiveMinimum})
76+
v.ExclusiveMinimum = false
77+
}
78+
if v.MultipleOf != nil {
79+
done = append(done, clearedValidation{Validation: "multipleOf", Value: v.MultipleOf})
80+
v.MultipleOf = nil
81+
}
82+
}
83+
84+
// ClearStringValidations clears all string validations.
85+
//
86+
// Some callbacks may be set by the caller to capture changed values.
87+
func (v *CommonValidations) ClearStringValidations(cbs ...func(string, interface{})) {
88+
done := make(clearedValidations, 0, 3)
89+
defer func() {
90+
done.apply(cbs)
91+
}()
92+
93+
if v.Pattern != "" {
94+
done = append(done, clearedValidation{Validation: "pattern", Value: v.Pattern})
95+
v.Pattern = ""
96+
}
97+
if v.MinLength != nil {
98+
done = append(done, clearedValidation{Validation: "minLength", Value: v.MinLength})
99+
v.MinLength = nil
100+
}
101+
if v.MaxLength != nil {
102+
done = append(done, clearedValidation{Validation: "maxLength", Value: v.MaxLength})
103+
v.MaxLength = nil
104+
}
105+
}
106+
107+
// ClearArrayValidations clears all array validations.
108+
//
109+
// Some callbacks may be set by the caller to capture changed values.
110+
func (v *CommonValidations) ClearArrayValidations(cbs ...func(string, interface{})) {
111+
done := make(clearedValidations, 0, 3)
112+
defer func() {
113+
done.apply(cbs)
114+
}()
115+
116+
if v.MaxItems != nil {
117+
done = append(done, clearedValidation{Validation: "maxItems", Value: v.MaxItems})
118+
v.MaxItems = nil
119+
}
120+
if v.MinItems != nil {
121+
done = append(done, clearedValidation{Validation: "minItems", Value: v.MinItems})
122+
v.MinItems = nil
123+
}
124+
if v.UniqueItems {
125+
done = append(done, clearedValidation{Validation: "uniqueItems", Value: v.UniqueItems})
126+
v.UniqueItems = false
127+
}
128+
}
129+
130+
// Validations returns a clone of the validations for a simple schema.
131+
//
132+
// NOTE: in the context of simple schema objects, MinProperties, MaxProperties
133+
// and PatternProperties remain unset.
134+
func (v CommonValidations) Validations() SchemaValidations {
135+
return SchemaValidations{
136+
CommonValidations: v,
137+
}
138+
}
139+
140+
// HasNumberValidations indicates if the validations are for numbers or integers
141+
func (v CommonValidations) HasNumberValidations() bool {
142+
return v.Maximum != nil || v.Minimum != nil || v.MultipleOf != nil
143+
}
144+
145+
// HasStringValidations indicates if the validations are for strings
146+
func (v CommonValidations) HasStringValidations() bool {
147+
return v.MaxLength != nil || v.MinLength != nil || v.Pattern != ""
148+
}
149+
150+
// HasArrayValidations indicates if the validations are for arrays
151+
func (v CommonValidations) HasArrayValidations() bool {
152+
return v.MaxItems != nil || v.MinItems != nil || v.UniqueItems
153+
}
154+
155+
// HasEnum indicates if the validation includes some enum constraint
156+
func (v CommonValidations) HasEnum() bool {
157+
return len(v.Enum) > 0
158+
}
159+
160+
// SchemaValidations describes the validation properties of a schema
161+
//
162+
// NOTE: at this moment, this is not embedded in SchemaProps because this would induce a breaking change
163+
// in the exported members: all initializers using litterals would fail.
164+
type SchemaValidations struct {
165+
CommonValidations
166+
167+
PatternProperties SchemaProperties `json:"patternProperties,omitempty"`
168+
MaxProperties *int64 `json:"maxProperties,omitempty"`
169+
MinProperties *int64 `json:"minProperties,omitempty"`
170+
}
171+
172+
// HasObjectValidations indicates if the validations are for objects
173+
func (v SchemaValidations) HasObjectValidations() bool {
174+
return v.MaxProperties != nil || v.MinProperties != nil || v.PatternProperties != nil
175+
}
176+
177+
// SetValidations for schema validations
178+
func (v *SchemaValidations) SetValidations(val SchemaValidations) {
179+
v.CommonValidations.SetValidations(val)
180+
v.PatternProperties = val.PatternProperties
181+
v.MaxProperties = val.MaxProperties
182+
v.MinProperties = val.MinProperties
183+
}
184+
185+
// Validations for a schema
186+
func (v SchemaValidations) Validations() SchemaValidations {
187+
val := v.CommonValidations.Validations()
188+
val.PatternProperties = v.PatternProperties
189+
val.MinProperties = v.MinProperties
190+
val.MaxProperties = v.MaxProperties
191+
return val
192+
}
193+
194+
// ClearObjectValidations returns a clone of the validations with all object validations cleared.
195+
//
196+
// Some callbacks may be set by the caller to capture changed values.
197+
func (v *SchemaValidations) ClearObjectValidations(cbs ...func(string, interface{})) {
198+
done := make(clearedValidations, 0, 3)
199+
defer func() {
200+
done.apply(cbs)
201+
}()
202+
203+
if v.MaxProperties != nil {
204+
done = append(done, clearedValidation{Validation: "maxProperties", Value: v.MaxProperties})
205+
v.MaxProperties = nil
206+
}
207+
if v.MinProperties != nil {
208+
done = append(done, clearedValidation{Validation: "minProperties", Value: v.MinProperties})
209+
v.MinProperties = nil
210+
}
211+
if v.PatternProperties != nil {
212+
done = append(done, clearedValidation{Validation: "patternProperties", Value: v.PatternProperties})
213+
v.PatternProperties = nil
214+
}
215+
}

0 commit comments

Comments
 (0)