Skip to content

Commit

Permalink
add json validation rule (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
rtbe authored Apr 11, 2024
1 parent 437573a commit be6217e
Show file tree
Hide file tree
Showing 4 changed files with 339 additions and 3 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ go 1.21

require (
github.com/gofrs/uuid v4.4.0+incompatible
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.8.4
golang.org/x/net v0.21.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/text v0.14.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
Expand Down
97 changes: 97 additions & 0 deletions json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package validator

import (
"context"
"encoding/json"
"fmt"
)

type JSON struct {
message string

whenFunc WhenFunc
skipEmpty bool
skipError bool
}

func NewJSON() *JSON {
return &JSON{
message: "Must be a valid JSON",
}
}

func (j *JSON) ValidateValue(_ context.Context, value any) error {
var bytes []byte

switch v := value.(type) {
case string:
bytes = []byte(v)
case *string:
bytes = []byte(*v)
case []byte:
bytes = v
case *[]byte:
bytes = *v
case json.RawMessage:
bytes = v
case *json.RawMessage:
bytes = *v
case fmt.Stringer:
if i, ok := v.(fmt.Stringer); ok {
bytes = []byte(i.String())
}
default:
return NewResult().WithError(NewValidationError(j.message))
}

if isValid := json.Valid(bytes); !isValid {
return NewResult().WithError(NewValidationError(j.message))
}

return nil
}

func (j *JSON) When(v WhenFunc) *JSON {
rc := *j
rc.whenFunc = v

return &rc
}

func (j *JSON) when() WhenFunc {
return j.whenFunc
}

func (j *JSON) setWhen(v WhenFunc) {
j.whenFunc = v
}

func (j *JSON) SkipOnEmpty() *JSON {
rc := *j
rc.skipEmpty = true

return &rc
}

func (j *JSON) skipOnEmpty() bool {
return j.skipEmpty
}

func (j *JSON) setSkipOnEmpty(v bool) {
j.skipEmpty = v
}

func (j *JSON) SkipOnError() *JSON {
rs := *j
rs.skipError = true

return &rs
}

func (j *JSON) shouldSkipOnError() bool {
return j.skipError
}

func (j *JSON) setSkipOnError(v bool) {
j.skipError = v
}
241 changes: 241 additions & 0 deletions json_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
package validator

import (
"context"
"encoding/json"
"testing"

"github.com/stretchr/testify/require"
)

type stringer string

func (s stringer) String() string {
return string(s)
}

func TestJSON_ValidateValue_Successfully(t *testing.T) {
// region data provider
ctx := context.Background()

str := `"hello world"`
obj := `{"hello":"world"}`
arr := `["hello","world"]`

bytesWithString := []byte(str)
bytesWithObject := []byte(obj)
bytesWithArray := []byte(arr)

jsonRawWithString := json.RawMessage(str)
jsonRawWithObject := json.RawMessage(obj)
jsonRawWithArray := json.RawMessage(arr)

type args struct {
value any
}
tests := []struct {
name string
args args
}{
{
name: "stringer with string",
args: args{value: stringer(str)},
},
{
name: "string",
args: args{value: str},
},
{
name: "string object",
args: args{value: obj},
},
{
name: "string array",
args: args{value: arr},
},
{
name: "string pointer",
args: args{value: &str},
},
{
name: "string pointer object",
args: args{value: &obj},
},
{
name: "string pointer array",
args: args{value: &arr},
},
{
name: "bytes with string",
args: args{value: bytesWithString},
},
{
name: "bytes with object",
args: args{value: bytesWithObject},
},
{
name: "bytes with array",
args: args{value: bytesWithArray},
},
{
name: "bytes pointer with string",
args: args{value: &bytesWithString},
},
{
name: "bytes with object",
args: args{value: &bytesWithObject},
},
{
name: "bytes with array",
args: args{value: &bytesWithArray},
},
{
name: "json.RawMessage with string",
args: args{value: jsonRawWithString},
},
{
name: "json.RawMessage with object",
args: args{value: jsonRawWithObject},
},
{
name: "json.RawMessage with array",
args: args{value: jsonRawWithArray},
},
{
name: "json.RawMessage pointer with string",
args: args{value: &jsonRawWithString},
},
{
name: "json.RawMessage pointer with object",
args: args{value: &jsonRawWithObject},
},
{
name: "json.RawMessage pointer with array",
args: args{value: &jsonRawWithArray},
},
}
// endregion

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
j := NewJSON()
err := j.ValidateValue(ctx, tt.args.value)
require.NoError(t, err)
})
}
}

func TestJSON_ValidateValue_Failure(t *testing.T) {
// region data provider
ctx := context.Background()

str := `hello world`
obj := `{hello":"world"`
arr := `["hello","world`

bytesWithString := []byte(str)
bytesWithObject := []byte(obj)
bytesWithArray := []byte(arr)

jsonRawWithString := json.RawMessage(str)
jsonRawWithObject := json.RawMessage(obj)
jsonRawWithArray := json.RawMessage(arr)

type args struct {
value any
}
tests := []struct {
name string
args args
}{
{
name: "int",
args: args{value: 6},
},
{
name: "stringer with string",
args: args{value: stringer(str)},
},
{
name: "string",
args: args{value: str},
},
{
name: "string object",
args: args{value: obj},
},
{
name: "string array",
args: args{value: arr},
},
{
name: "string pointer",
args: args{value: &str},
},
{
name: "string pointer object",
args: args{value: &obj},
},
{
name: "string pointer array",
args: args{value: &arr},
},
{
name: "bytes with string",
args: args{value: bytesWithString},
},
{
name: "bytes with object",
args: args{value: bytesWithObject},
},
{
name: "bytes with array",
args: args{value: bytesWithArray},
},
{
name: "bytes pointer with string",
args: args{value: &bytesWithString},
},
{
name: "bytes with object",
args: args{value: &bytesWithObject},
},
{
name: "bytes with array",
args: args{value: &bytesWithArray},
},
{
name: "json.RawMessage with string",
args: args{value: jsonRawWithString},
},
{
name: "json.RawMessage with object",
args: args{value: jsonRawWithObject},
},
{
name: "json.RawMessage with array",
args: args{value: jsonRawWithArray},
},
{
name: "json.RawMessage pointer with string",
args: args{value: &jsonRawWithString},
},
{
name: "json.RawMessage pointer with object",
args: args{value: &jsonRawWithObject},
},
{
name: "json.RawMessage pointer with array",
args: args{value: &jsonRawWithArray},
},
}
// endregion

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
j := NewJSON()
err := j.ValidateValue(ctx, tt.args.value)
require.Error(t, err)
})
}
}

0 comments on commit be6217e

Please sign in to comment.