From db56162a0dc8757e169dffa5f3d90605b29a5ce7 Mon Sep 17 00:00:00 2001 From: Matthew Wood Date: Fri, 3 Dec 2021 17:19:47 -0600 Subject: [PATCH] fixes #9 implement pattern validation on strings (#11) Co-authored-by: Matthew Wood --- field.go | 18 ++++++++++++++++++ field_test.go | 20 ++++++++++++++++++++ swagger.go | 2 ++ 3 files changed, 40 insertions(+) diff --git a/field.go b/field.go index f221563..811fb4b 100644 --- a/field.go +++ b/field.go @@ -3,6 +3,7 @@ package crud import ( "fmt" "reflect" + "regexp" "strings" "time" ) @@ -14,6 +15,7 @@ type Field struct { obj map[string]Field max *float64 min *float64 + pattern *regexp.Regexp required *bool example interface{} description string @@ -64,6 +66,7 @@ var ( errMinimum = fmt.Errorf("minimum exceeded") errEnumNotFound = fmt.Errorf("value not in enum") errUnknown = fmt.Errorf("unknown value") + errPattern = fmt.Errorf("value does not match pattern") ) // Validate is used in the validation middleware to tell if the value passed @@ -115,6 +118,9 @@ func (f *Field) Validate(value interface{}) error { if f.min != nil && len(v) < int(*f.min) { return errMinimum } + if f.pattern != nil && !f.pattern.MatchString(v) { + return errPattern + } switch f.format { case FormatDateTime: _, err := time.Parse(time.RFC3339, v) @@ -328,6 +334,12 @@ func (f Field) Max(max float64) Field { return f } +// Pattern specifies a regex pattern value for this field +func (f Field) Pattern(pattern string) Field { + f.pattern = regexp.MustCompile(pattern) + return f +} + // Required specifies the field must be provided. Can't be used with Default. func (f Field) Required() Field { if f._default != nil { @@ -460,6 +472,9 @@ func (f *Field) ToSwaggerParameters(in string) (parameters []Parameter) { Minimum: field.min, Maximum: field.max, } + if field.pattern != nil { + param.Pattern = field.pattern.String() + } if field.kind == KindArray { if field.arr != nil { temp := field.arr.ToJsonSchema() @@ -518,6 +533,9 @@ func (f *Field) ToJsonSchema() JsonSchema { if field.max != nil { prop.Maximum = *field.max } + if field.pattern != nil { + prop.Pattern = field.pattern.String() + } if prop.Type == KindArray { if field.arr != nil { items := field.arr.ToJsonSchema() diff --git a/field_test.go b/field_test.go index 9b03432..9992653 100644 --- a/field_test.go +++ b/field_test.go @@ -27,6 +27,16 @@ func TestField_Min_Panic(t *testing.T) { String().Max(1).Min(2) } +func TestField_Pattern_Panic(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("The code did not panic") + } + }() + + String().Pattern("[") +} + func TestField_String(t *testing.T) { table := []struct { Field Field @@ -93,6 +103,16 @@ func TestField_String(t *testing.T) { Input: "hi", Expected: nil, }, + { + Field: String().Pattern("[a-c]"), + Input: "abc", + Expected: nil, + }, + { + Field: String().Pattern("[a-c]"), + Input: "def", + Expected: errPattern, + }, } for i, test := range table { diff --git a/swagger.go b/swagger.go index 98c256b..23b812e 100644 --- a/swagger.go +++ b/swagger.go @@ -26,6 +26,7 @@ type JsonSchema struct { Maximum float64 `json:"maximum,omitempty"` Enum []interface{} `json:"enum,omitempty"` Default interface{} `json:"default,omitempty"` + Pattern string `json:"pattern,omitempty"` } type Path struct { @@ -58,6 +59,7 @@ type Parameter struct { Minimum *float64 `json:"minimum,omitempty"` Maximum *float64 `json:"maximum,omitempty"` Enum []interface{} `json:"enum,omitempty"` + Pattern string `json:"pattern,omitempty"` Default interface{} `json:"default,omitempty"` Items *JsonSchema `json:"items,omitempty"` CollectionFormat string `json:"collectionFormat,omitempty"`