Add jsonschema Validate
This commit is contained in:
34
check_valid.go
Normal file
34
check_valid.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package commons
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
|
||||
"github.com/ianlancetaylor/jsonschema"
|
||||
"github.com/ianlancetaylor/jsonschema/draft7"
|
||||
)
|
||||
|
||||
func Validate(schema string, dataAny any) error {
|
||||
data := StructToMapRecursive(dataAny)
|
||||
|
||||
var v any
|
||||
if err := json.Unmarshal([]byte(schema), &v); err != nil {
|
||||
log.Printf("Failed to decode json %#v", err)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
vldtor, err := jsonschema.SchemaFromJSON(draft7.SchemaID, nil, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// validate
|
||||
valid := vldtor.Validate(data)
|
||||
|
||||
if valid != nil {
|
||||
return valid
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
46
check_valid_test.go
Normal file
46
check_valid_test.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package commons
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
const sampleSchema = `
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"age": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": ["id", "name"]
|
||||
}
|
||||
`
|
||||
|
||||
func TestRequiredName (t *testing.T) {
|
||||
data := map[string]any {
|
||||
"id": "myid",
|
||||
"age": 100,
|
||||
}
|
||||
|
||||
if err := Validate(sampleSchema, data); err == nil || err.Error() != `required/name: missing required field "name"` {
|
||||
t.Fatalf("Should throw `%s` but got %s", "required/name: missing required field \"name\"", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPassingValidate (t *testing.T) {
|
||||
data := map[string]any {
|
||||
"id": "myid",
|
||||
"name": "Hey",
|
||||
"age": 100,
|
||||
}
|
||||
|
||||
if err := Validate(sampleSchema, data); err != nil {
|
||||
t.Fatal("Should have not thrown an error")
|
||||
}
|
||||
}
|
||||
5
go.mod
5
go.mod
@@ -2,4 +2,7 @@ module git.gsuntres.com/general/commons
|
||||
|
||||
go 1.25.0
|
||||
|
||||
require go.mongodb.org/mongo-driver/v2 v2.5.0
|
||||
require (
|
||||
github.com/ianlancetaylor/jsonschema v0.0.0-20251021232724-46ecbf32a0a5
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0
|
||||
)
|
||||
|
||||
2
go.sum
2
go.sum
@@ -2,5 +2,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/ianlancetaylor/jsonschema v0.0.0-20251021232724-46ecbf32a0a5 h1:x2QxKV4w/sBEwwUUmBH/8cFjeOBZqwaB5dz5rcuFspU=
|
||||
github.com/ianlancetaylor/jsonschema v0.0.0-20251021232724-46ecbf32a0a5/go.mod h1:KtN3dTgXsLnC5GJBRNmOPd/HUInNcQ84lUCrKJPrvDc=
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
|
||||
|
||||
42
struct.go
42
struct.go
@@ -4,10 +4,50 @@ import (
|
||||
"log"
|
||||
"reflect"
|
||||
"strings"
|
||||
// "encoding/json"
|
||||
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
)
|
||||
|
||||
// StructToMapRecursive given a struct or a primitive will return the equivalent
|
||||
// map[string]any of the struct or the primitive as is.
|
||||
func StructToMapRecursive(obj any) any {
|
||||
v := reflect.ValueOf(obj)
|
||||
|
||||
// Handle pointers by getting the underlying element
|
||||
if v.Kind() == reflect.Ptr {
|
||||
if v.IsNil() { return nil }
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Struct:
|
||||
result := make(map[string]any)
|
||||
t := v.Type()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
// Skip unexported fields (private fields)
|
||||
if field.PkgPath != "" { continue }
|
||||
|
||||
// Recurse into the field's value
|
||||
result[field.Name] = StructToMapRecursive(v.Field(i).Interface())
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
case reflect.Slice, reflect.Array:
|
||||
result := make([]any, v.Len())
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
result[i] = StructToMapRecursive(v.Index(i).Interface())
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
default:
|
||||
// Return basic types (int, string, etc.) as is
|
||||
return obj
|
||||
}
|
||||
}
|
||||
|
||||
// MapToStruct will convert a map[string]any to a struct.
|
||||
func MapToStruct(m map[string]any, o any) error {
|
||||
b, err := bson.Marshal(m)
|
||||
|
||||
@@ -4,6 +4,42 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStructToMapRecursive(t *testing.T) {
|
||||
type O struct {
|
||||
Name string
|
||||
Age int
|
||||
Active bool
|
||||
}
|
||||
|
||||
o := &O{
|
||||
Name: "Nick",
|
||||
Age: 15,
|
||||
Active: true,
|
||||
}
|
||||
|
||||
mAny := StructToMapRecursive(o)
|
||||
|
||||
switch mAny.(type) {
|
||||
case map[string]any:
|
||||
m := mAny.(map[string]any)
|
||||
if v, ok := m["Name"]; !ok || v != "Nick" {
|
||||
t.Fatalf("Unexpected map %v", v)
|
||||
}
|
||||
|
||||
if v, ok := m["Age"]; !ok || v != 15 {
|
||||
t.Fatalf("Unexpected map %v", v)
|
||||
}
|
||||
|
||||
if v, ok := m["Active"]; !ok || v != true {
|
||||
t.Fatalf("Unexpected map %v", v)
|
||||
}
|
||||
default:
|
||||
t.Fatal("is not map")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
func TestMapToStruct(t *testing.T) {
|
||||
o := map[string]any {
|
||||
"name": "name1",
|
||||
|
||||
Reference in New Issue
Block a user