Skip to content
This repository has been archived by the owner on Feb 5, 2020. It is now read-only.

Commit

Permalink
Merge pull request #9 from ripienaar/7
Browse files Browse the repository at this point in the history
(#7) Support validating Go time.Duration durations
  • Loading branch information
ripienaar authored Mar 26, 2018
2 parents 99e0f79 + 51d3cc3 commit 4ce2ef2
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 15 deletions.
26 changes: 26 additions & 0 deletions duration/duration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package duration

import (
"fmt"
"reflect"
"time"
)

// ValidateString validates that input is a valid duration
func ValidateString(input string) (bool, error) {
_, err := time.ParseDuration(input)
if err != nil {
return false, err
}

return true, nil
}

// ValidateStructField validates a struct field holds a valid duration
func ValidateStructField(value reflect.Value, tag string) (bool, error) {
if value.Kind() != reflect.String {
return false, fmt.Errorf("only strings can be Duration validated")
}

return ValidateString(value.String())
}
54 changes: 54 additions & 0 deletions duration/duration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package duration

import (
"reflect"
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

func TestFileContent(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Validator/Duration")
}

var _ = Describe("ValidateString", func() {
It("Should match durations correctly", func() {
ok, err := ValidateString("1s")
Expect(err).ToNot(HaveOccurred())
Expect(ok).To(BeTrue())

ok, err = ValidateString("1h")
Expect(err).ToNot(HaveOccurred())
Expect(ok).To(BeTrue())

ok, err = ValidateString("1w")
Expect(err).To(MatchError("time: unknown unit w in duration 1w"))
Expect(ok).To(BeFalse())
})
})

var _ = Describe("ValidateStructField", func() {
type t struct {
Interval string `validate:"duration"`
}

It("Should validate the struct correctly", func() {
st := t{"1h"}

val := reflect.ValueOf(st)
valueField := val.FieldByName("Interval")
typeField, _ := val.Type().FieldByName("Interval")

ok, err := ValidateStructField(valueField, typeField.Tag.Get("validate"))
Expect(err).ToNot(HaveOccurred())
Expect(ok).To(BeTrue())

st.Interval = "foo"
valueField = reflect.ValueOf(st).FieldByName("Interval")
ok, err = ValidateStructField(valueField, typeField.Tag.Get("validate"))
Expect(err).To(MatchError("time: invalid duration foo"))
Expect(ok).To(BeFalse())
})
})
6 changes: 6 additions & 0 deletions validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"reflect"
"strings"

"github.com/choria-io/go-validator/duration"
"github.com/choria-io/go-validator/enum"
"github.com/choria-io/go-validator/ipaddress"
"github.com/choria-io/go-validator/ipv4"
Expand Down Expand Up @@ -116,6 +117,11 @@ func validateStructField(valueField reflect.Value, typeField reflect.StructField
if ok, err := enum.ValidateStructField(valueField, validation); !ok {
return fmt.Errorf("%s enum validation failed: %s", typeField.Name, err)
}

} else if strings.HasPrefix(validation, "duration") {
if ok, err := duration.ValidateStructField(valueField, validation); !ok {
return fmt.Errorf("%s duration validation failed: %s", typeField.Name, err)
}
}

return nil
Expand Down
41 changes: 26 additions & 15 deletions validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ type nest struct {
}

type vdata struct {
SS string `validate:"shellsafe"`
ML string `validate:"maxlength=3"`
Enum []string `validate:"enum=one,two"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
IP string `validate:"ipaddress"`
RE string `validate:"regex=world$"`
SS string `validate:"shellsafe"`
ML string `validate:"maxlength=3"`
Enum []string `validate:"enum=one,two"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
IP string `validate:"ipaddress"`
RE string `validate:"regex=world$"`
Duration string `validate:"duration"`
nest
}

Expand All @@ -34,10 +35,11 @@ var s vdata
var _ = Describe("ValidateStructField", func() {
BeforeEach(func() {
s = vdata{
IPv4: "1.2.3.4",
IPv6: "2a00:1450:4003:807::200e",
IP: "1.2.3.4",
RE: "hello world",
IPv4: "1.2.3.4",
IPv6: "2a00:1450:4003:807::200e",
IP: "1.2.3.4",
RE: "hello world",
Duration: "1h",
}
})

Expand All @@ -58,10 +60,11 @@ var _ = Describe("ValidateStructField", func() {
var _ = Describe("ValidateStruct", func() {
BeforeEach(func() {
s = vdata{
IPv4: "1.2.3.4",
IPv6: "2a00:1450:4003:807::200e",
IP: "1.2.3.4",
RE: "hello world",
IPv4: "1.2.3.4",
IPv6: "2a00:1450:4003:807::200e",
IP: "1.2.3.4",
RE: "hello world",
Duration: "1h",
}
})

Expand Down Expand Up @@ -130,4 +133,12 @@ var _ = Describe("ValidateStruct", func() {
Expect(err).To(MatchError("RE regular expression validation failed: input does not match 'world$'"))
Expect(ok).To(BeFalse())
})

It("Should support regex", func() {
s.Duration = "1w"
ok, err := validator.ValidateStruct(s)

Expect(err).To(MatchError("Duration duration validation failed: time: unknown unit w in duration 1w"))
Expect(ok).To(BeFalse())
})
})

0 comments on commit 4ce2ef2

Please sign in to comment.