Compare commits

..

No commits in common. "6182276856ccebcab3a64f77733b06e194890961" and "0ee814abbe9ee7de10f730db56957b2d593eac4b" have entirely different histories.

24 changed files with 203 additions and 249 deletions

View File

@ -89,16 +89,16 @@ import (
"github.com/xdrm-io/aicra" "github.com/xdrm-io/aicra"
"github.com/xdrm-io/aicra/api" "github.com/xdrm-io/aicra/api"
"github.com/xdrm-io/aicra/validator/builtin" "github.com/xdrm-io/aicra/datatype/builtin"
) )
func main() { func main() {
builder := &aicra.Builder{} builder := &aicra.Builder{}
// add custom type validators // register data validators
builder.Validate(validator.BoolDataType{}) builder.AddType(builtin.BoolDataType{})
builder.Validate(validator.UintDataType{}) builder.AddType(builtin.UintDataType{})
builder.Validate(validator.StringDataType{}) builder.AddType(builtin.StringDataType{})
// load your configuration // load your configuration
config, err := os.Open("api.json") config, err := os.Open("api.json")

View File

@ -5,9 +5,9 @@ import (
"io" "io"
"net/http" "net/http"
"github.com/xdrm-io/aicra/datatype"
"github.com/xdrm-io/aicra/internal/config" "github.com/xdrm-io/aicra/internal/config"
"github.com/xdrm-io/aicra/internal/dynfunc" "github.com/xdrm-io/aicra/internal/dynfunc"
"github.com/xdrm-io/aicra/validator"
) )
// Builder for an aicra server // Builder for an aicra server
@ -31,18 +31,18 @@ type apiHandler struct {
dyn *dynfunc.Handler dyn *dynfunc.Handler
} }
// Validate adds an available Type to validate in the api definition // AddType adds an available datatype to the api definition
func (b *Builder) Validate(t validator.Type) error { func (b *Builder) AddType(t datatype.T) error {
if b.conf == nil { if b.conf == nil {
b.conf = &config.Server{} b.conf = &config.Server{}
} }
if b.conf.Services != nil { if b.conf.Services != nil {
return errLateType return errLateType
} }
if b.conf.Validators == nil { if b.conf.Types == nil {
b.conf.Validators = make([]validator.Type, 0) b.conf.Types = make([]datatype.T, 0)
} }
b.conf.Validators = append(b.conf.Validators, t) b.conf.Types = append(b.conf.Types, t)
return nil return nil
} }

View File

@ -8,26 +8,26 @@ import (
"testing" "testing"
"github.com/xdrm-io/aicra/api" "github.com/xdrm-io/aicra/api"
"github.com/xdrm-io/aicra/validator" "github.com/xdrm-io/aicra/datatype/builtin"
) )
func addBuiltinTypes(b *Builder) error { func addBuiltinTypes(b *Builder) error {
if err := b.Validate(validator.AnyType{}); err != nil { if err := b.AddType(builtin.AnyDataType{}); err != nil {
return err return err
} }
if err := b.Validate(validator.BoolType{}); err != nil { if err := b.AddType(builtin.BoolDataType{}); err != nil {
return err return err
} }
if err := b.Validate(validator.FloatType{}); err != nil { if err := b.AddType(builtin.FloatDataType{}); err != nil {
return err return err
} }
if err := b.Validate(validator.IntType{}); err != nil { if err := b.AddType(builtin.IntDataType{}); err != nil {
return err return err
} }
if err := b.Validate(validator.StringType{}); err != nil { if err := b.AddType(builtin.StringDataType{}); err != nil {
return err return err
} }
if err := b.Validate(validator.UintType{}); err != nil { if err := b.AddType(builtin.UintDataType{}); err != nil {
return err return err
} }
return nil return nil
@ -35,7 +35,7 @@ func addBuiltinTypes(b *Builder) error {
func TestAddType(t *testing.T) { func TestAddType(t *testing.T) {
builder := &Builder{} builder := &Builder{}
err := builder.Validate(validator.BoolType{}) err := builder.AddType(builtin.BoolDataType{})
if err != nil { if err != nil {
t.Fatalf("unexpected error: %s", err) t.Fatalf("unexpected error: %s", err)
} }
@ -43,7 +43,7 @@ func TestAddType(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %s", err) t.Fatalf("unexpected error: %s", err)
} }
err = builder.Validate(validator.FloatType{}) err = builder.AddType(builtin.FloatDataType{})
if err != errLateType { if err != errLateType {
t.Fatalf("expected <%v> got <%v>", errLateType, err) t.Fatalf("expected <%v> got <%v>", errLateType, err)
} }

26
datatype/builtin/any.go Normal file
View File

@ -0,0 +1,26 @@
package builtin
import (
"reflect"
"github.com/xdrm-io/aicra/datatype"
)
// AnyDataType is what its name tells
type AnyDataType struct{}
// Type returns the type of data
func (AnyDataType) Type() reflect.Type {
return reflect.TypeOf(interface{}(nil))
}
// Build returns the validator
func (AnyDataType) Build(typeName string, registry ...datatype.T) datatype.Validator {
// nothing if type not handled
if typeName != "any" {
return nil
}
return func(value interface{}) (interface{}, bool) {
return value, true
}
}

View File

@ -1,16 +1,16 @@
package validator_test package builtin_test
import ( import (
"fmt" "fmt"
"testing" "testing"
"github.com/xdrm-io/aicra/validator" "github.com/xdrm-io/aicra/datatype/builtin"
) )
func TestAny_AvailableTypes(t *testing.T) { func TestAny_AvailableTypes(t *testing.T) {
t.Parallel() t.Parallel()
dt := validator.AnyType{} dt := builtin.AnyDataType{}
tests := []struct { tests := []struct {
Type string Type string
@ -26,7 +26,7 @@ func TestAny_AvailableTypes(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
validator := dt.Validator(test.Type) validator := dt.Build(test.Type)
if validator == nil { if validator == nil {
if test.Handled { if test.Handled {
@ -47,7 +47,7 @@ func TestAny_AlwaysTrue(t *testing.T) {
const typeName = "any" const typeName = "any"
validator := validator.AnyType{}.Validator(typeName) validator := builtin.AnyDataType{}.Build(typeName)
if validator == nil { if validator == nil {
t.Errorf("expect %q to be handled", typeName) t.Errorf("expect %q to be handled", typeName)
t.Fail() t.Fail()

View File

@ -1,24 +1,23 @@
package validator package builtin
import ( import (
"reflect" "reflect"
"github.com/xdrm-io/aicra/datatype"
) )
// BoolType makes the "bool" type available in the aicra configuration // BoolDataType is what its name tells
// It considers valid: type BoolDataType struct{}
// - booleans
// - strings containing "true" or "false"
// - []byte containing "true" or "false"
type BoolType struct{}
// GoType returns the `bool` type // Type returns the type of data
func (BoolType) GoType() reflect.Type { func (BoolDataType) Type() reflect.Type {
return reflect.TypeOf(true) return reflect.TypeOf(true)
} }
// Validator for bool values // Build returns the validator
func (BoolType) Validator(typename string, avail ...Type) ValidateFunc { func (BoolDataType) Build(typeName string, registry ...datatype.T) datatype.Validator {
if typename != "bool" { // nothing if type not handled
if typeName != "bool" {
return nil return nil
} }

View File

@ -1,16 +1,16 @@
package validator_test package builtin_test
import ( import (
"fmt" "fmt"
"testing" "testing"
"github.com/xdrm-io/aicra/validator" "github.com/xdrm-io/aicra/datatype/builtin"
) )
func TestBool_AvailableTypes(t *testing.T) { func TestBool_AvailableTypes(t *testing.T) {
t.Parallel() t.Parallel()
dt := validator.BoolType{} dt := builtin.BoolDataType{}
tests := []struct { tests := []struct {
Type string Type string
@ -26,7 +26,7 @@ func TestBool_AvailableTypes(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.Type, func(t *testing.T) { t.Run(test.Type, func(t *testing.T) {
validator := dt.Validator(test.Type) validator := dt.Build(test.Type)
if validator == nil { if validator == nil {
if test.Handled { if test.Handled {
t.Errorf("expect %q to be handled", test.Type) t.Errorf("expect %q to be handled", test.Type)
@ -49,7 +49,7 @@ func TestBool_Values(t *testing.T) {
const typeName = "bool" const typeName = "bool"
validator := validator.BoolType{}.Validator(typeName) validator := builtin.BoolDataType{}.Build(typeName)
if validator == nil { if validator == nil {
t.Errorf("expect %q to be handled", typeName) t.Errorf("expect %q to be handled", typeName)
t.Fail() t.Fail()

View File

@ -1,27 +1,24 @@
package validator package builtin
import ( import (
"encoding/json" "encoding/json"
"reflect" "reflect"
"github.com/xdrm-io/aicra/datatype"
) )
// FloatType makes the "float" (or "float64") type available in the aicra configuration // FloatDataType is what its name tells
// It considers valid: type FloatDataType struct{}
// - float64
// - int (since it does not overflow)
// - uint (since it does not overflow)
// - strings containing json-compatible floats
// - []byte containing json-compatible floats
type FloatType struct{}
// GoType returns the `float64` type // Type returns the type of data
func (FloatType) GoType() reflect.Type { func (FloatDataType) Type() reflect.Type {
return reflect.TypeOf(float64(0)) return reflect.TypeOf(float64(0))
} }
// Validator for float64 values // Build returns the validator
func (FloatType) Validator(typename string, avail ...Type) ValidateFunc { func (FloatDataType) Build(typeName string, registry ...datatype.T) datatype.Validator {
if typename != "float64" && typename != "float" { // nothing if type not handled
if typeName != "float64" && typeName != "float" {
return nil return nil
} }
return func(value interface{}) (interface{}, bool) { return func(value interface{}) (interface{}, bool) {

View File

@ -1,17 +1,17 @@
package validator_test package builtin_test
import ( import (
"fmt" "fmt"
"math" "math"
"testing" "testing"
"github.com/xdrm-io/aicra/validator" "github.com/xdrm-io/aicra/datatype/builtin"
) )
func TestFloat64_AvailableTypes(t *testing.T) { func TestFloat64_AvailableTypes(t *testing.T) {
t.Parallel() t.Parallel()
dt := validator.FloatType{} dt := builtin.FloatDataType{}
tests := []struct { tests := []struct {
Type string Type string
@ -33,7 +33,7 @@ func TestFloat64_AvailableTypes(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.Type, func(t *testing.T) { t.Run(test.Type, func(t *testing.T) {
validator := dt.Validator(test.Type) validator := dt.Build(test.Type)
if validator == nil { if validator == nil {
if test.Handled { if test.Handled {
t.Errorf("expect %q to be handled", test.Type) t.Errorf("expect %q to be handled", test.Type)
@ -56,7 +56,7 @@ func TestFloat64_Values(t *testing.T) {
const typeName = "float" const typeName = "float"
validator := validator.FloatType{}.Validator(typeName) validator := builtin.FloatDataType{}.Build(typeName)
if validator == nil { if validator == nil {
t.Errorf("expect %q to be handled", typeName) t.Errorf("expect %q to be handled", typeName)
t.Fail() t.Fail()

View File

@ -1,29 +1,25 @@
package validator package builtin
import ( import (
"encoding/json" "encoding/json"
"math" "math"
"reflect" "reflect"
"github.com/xdrm-io/aicra/datatype"
) )
// IntType makes the "int" type available in the aicra configuration // IntDataType is what its name tells
// It considers valid: type IntDataType struct{}
// - int
// - float64 (since it does not overflow)
// - uint (since it does not overflow)
// - strings containing json-compatible integers
// - []byte containing json-compatible integers
type IntType struct{}
// GoType returns the `int` type // Type returns the type of data
func (IntType) GoType() reflect.Type { func (IntDataType) Type() reflect.Type {
return reflect.TypeOf(int(0)) return reflect.TypeOf(int(0))
} }
// Validator for int values // Build returns the validator
func (IntType) Validator(typename string, avail ...Type) ValidateFunc { func (IntDataType) Build(typeName string, registry ...datatype.T) datatype.Validator {
// nothing if type not handled // nothing if type not handled
if typename != "int" { if typeName != "int" {
return nil return nil
} }

View File

@ -1,17 +1,17 @@
package validator_test package builtin_test
import ( import (
"fmt" "fmt"
"math" "math"
"testing" "testing"
"github.com/xdrm-io/aicra/validator" "github.com/xdrm-io/aicra/datatype/builtin"
) )
func TestInt_AvailableTypes(t *testing.T) { func TestInt_AvailableTypes(t *testing.T) {
t.Parallel() t.Parallel()
dt := validator.IntType{} dt := builtin.IntDataType{}
tests := []struct { tests := []struct {
Type string Type string
@ -27,7 +27,7 @@ func TestInt_AvailableTypes(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.Type, func(t *testing.T) { t.Run(test.Type, func(t *testing.T) {
validator := dt.Validator(test.Type) validator := dt.Build(test.Type)
if validator == nil { if validator == nil {
if test.Handled { if test.Handled {
t.Errorf("expect %q to be handled", test.Type) t.Errorf("expect %q to be handled", test.Type)
@ -50,7 +50,7 @@ func TestInt_Values(t *testing.T) {
const typeName = "int" const typeName = "int"
validator := validator.IntType{}.Validator(typeName) validator := builtin.IntDataType{}.Build(typeName)
if validator == nil { if validator == nil {
t.Errorf("expect %q to be handled", typeName) t.Errorf("expect %q to be handled", typeName)
t.Fail() t.Fail()

View File

@ -1,37 +1,32 @@
package validator package builtin
import ( import (
"reflect" "reflect"
"regexp" "regexp"
"strconv" "strconv"
"github.com/xdrm-io/aicra/datatype"
) )
var ( var fixedLengthRegex = regexp.MustCompile(`^string\((\d+)\)$`)
fixedLengthRegex = regexp.MustCompile(`^string\((\d+)\)$`) var variableLengthRegex = regexp.MustCompile(`^string\((\d+), ?(\d+)\)$`)
variableLengthRegex = regexp.MustCompile(`^string\((\d+), ?(\d+)\)$`)
)
// StringType makes the types beloz available in the aicra configuration: // StringDataType is what its name tells
// - "string" considers any string valid type StringDataType struct{}
// - "string(n)" considers any string with an exact size of `n` valid
// - "string(a,b)" considers any string with a size between `a` and `b` valid
// > for the last one, `a` and `b` are included in the valid sizes
type StringType struct{}
// GoType returns the `string` type // Type returns the type of data
func (StringType) GoType() reflect.Type { func (StringDataType) Type() reflect.Type {
return reflect.TypeOf(string("")) return reflect.TypeOf(string(""))
} }
// Validator for strings with any/fixed/bound sizes // Build returns the validator.
func (s StringType) Validator(typename string, avail ...Type) ValidateFunc { // availables type names are : `string`, `string(length)` and `string(minLength, maxLength)`.
var ( func (s StringDataType) Build(typeName string, registry ...datatype.T) datatype.Validator {
simple = (typename == "string") simple := typeName == "string"
fixedLengthMatches = fixedLengthRegex.FindStringSubmatch(typename) fixedLengthMatches := fixedLengthRegex.FindStringSubmatch(typeName)
variableLengthMatches = variableLengthRegex.FindStringSubmatch(typename) variableLengthMatches := variableLengthRegex.FindStringSubmatch(typeName)
)
// ignore unknown typename // nothing if type not handled
if !simple && fixedLengthMatches == nil && variableLengthMatches == nil { if !simple && fixedLengthMatches == nil && variableLengthMatches == nil {
return nil return nil
} }
@ -45,7 +40,7 @@ func (s StringType) Validator(typename string, avail ...Type) ValidateFunc {
if fixedLengthMatches != nil { if fixedLengthMatches != nil {
exLen, ok := s.getFixedLength(fixedLengthMatches) exLen, ok := s.getFixedLength(fixedLengthMatches)
if !ok { if !ok {
return nil mustFail = true
} }
min = exLen min = exLen
max = exLen max = exLen
@ -54,7 +49,7 @@ func (s StringType) Validator(typename string, avail ...Type) ValidateFunc {
} else if variableLengthMatches != nil { } else if variableLengthMatches != nil {
exMin, exMax, ok := s.getVariableLength(variableLengthMatches) exMin, exMax, ok := s.getVariableLength(variableLengthMatches)
if !ok { if !ok {
return nil mustFail = true
} }
min = exMin min = exMin
max = exMax max = exMax
@ -89,7 +84,7 @@ func (s StringType) Validator(typename string, avail ...Type) ValidateFunc {
} }
// getFixedLength returns the fixed length from regex matches and a success state. // getFixedLength returns the fixed length from regex matches and a success state.
func (StringType) getFixedLength(regexMatches []string) (int, bool) { func (StringDataType) getFixedLength(regexMatches []string) (int, bool) {
// incoherence error // incoherence error
if regexMatches == nil || len(regexMatches) < 2 { if regexMatches == nil || len(regexMatches) < 2 {
return 0, false return 0, false
@ -105,7 +100,7 @@ func (StringType) getFixedLength(regexMatches []string) (int, bool) {
} }
// getVariableLength returns the length min and max from regex matches and a success state. // getVariableLength returns the length min and max from regex matches and a success state.
func (StringType) getVariableLength(regexMatches []string) (int, int, bool) { func (StringDataType) getVariableLength(regexMatches []string) (int, int, bool) {
// incoherence error // incoherence error
if regexMatches == nil || len(regexMatches) < 3 { if regexMatches == nil || len(regexMatches) < 3 {
return 0, 0, false return 0, 0, false

View File

@ -1,16 +1,16 @@
package validator_test package builtin_test
import ( import (
"fmt" "fmt"
"testing" "testing"
"github.com/xdrm-io/aicra/validator" "github.com/xdrm-io/aicra/datatype/builtin"
) )
func TestString_AvailableTypes(t *testing.T) { func TestString_AvailableTypes(t *testing.T) {
t.Parallel() t.Parallel()
dt := validator.StringType{} dt := builtin.StringDataType{}
tests := []struct { tests := []struct {
Type string Type string
@ -53,7 +53,7 @@ func TestString_AvailableTypes(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.Type, func(t *testing.T) { t.Run(test.Type, func(t *testing.T) {
validator := dt.Validator(test.Type) validator := dt.Build(test.Type)
if validator == nil { if validator == nil {
if test.Handled { if test.Handled {
@ -75,7 +75,7 @@ func TestString_AnyLength(t *testing.T) {
const typeName = "string" const typeName = "string"
validator := validator.StringType{}.Validator(typeName) validator := builtin.StringDataType{}.Build(typeName)
if validator == nil { if validator == nil {
t.Errorf("expect %q to be handled", typeName) t.Errorf("expect %q to be handled", typeName)
t.Fail() t.Fail()
@ -133,7 +133,7 @@ func TestString_FixedLength(t *testing.T) {
for i, test := range tests { for i, test := range tests {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
validator := validator.StringType{}.Validator(test.Type) validator := builtin.StringDataType{}.Build(test.Type)
if validator == nil { if validator == nil {
t.Errorf("expect %q to be handled", test.Type) t.Errorf("expect %q to be handled", test.Type)
t.Fail() t.Fail()
@ -194,7 +194,7 @@ func TestString_VariableLength(t *testing.T) {
for i, test := range tests { for i, test := range tests {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
validator := validator.StringType{}.Validator(test.Type) validator := builtin.StringDataType{}.Build(test.Type)
if validator == nil { if validator == nil {
t.Errorf("expect %q to be handled", test.Type) t.Errorf("expect %q to be handled", test.Type)
t.Fail() t.Fail()

View File

@ -1,28 +1,25 @@
package validator package builtin
import ( import (
"encoding/json" "encoding/json"
"math" "math"
"reflect" "reflect"
"github.com/xdrm-io/aicra/datatype"
) )
// UintType makes the "uint" type available in the aicra configuration // UintDataType is what its name tells
// It considers valid: type UintDataType struct{}
// - uint
// - int (since it does not overflow)
// - float64 (since it does not overflow)
// - strings containing json-compatible integers
// - []byte containing json-compatible integers
type UintType struct{}
// GoType returns the `uint` type // Type returns the type of data
func (UintType) GoType() reflect.Type { func (UintDataType) Type() reflect.Type {
return reflect.TypeOf(uint(0)) return reflect.TypeOf(uint(0))
} }
// Validator for uint values // Build returns the validator
func (UintType) Validator(other string, avail ...Type) ValidateFunc { func (UintDataType) Build(typeName string, registry ...datatype.T) datatype.Validator {
if other != "uint" { // nothing if type not handled
if typeName != "uint" {
return nil return nil
} }

View File

@ -1,17 +1,17 @@
package validator_test package builtin_test
import ( import (
"fmt" "fmt"
"math" "math"
"testing" "testing"
"github.com/xdrm-io/aicra/validator" "github.com/xdrm-io/aicra/datatype/builtin"
) )
func TestUint_AvailableTypes(t *testing.T) { func TestUint_AvailableTypes(t *testing.T) {
t.Parallel() t.Parallel()
dt := validator.UintType{} dt := builtin.UintDataType{}
tests := []struct { tests := []struct {
Type string Type string
@ -27,7 +27,7 @@ func TestUint_AvailableTypes(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.Type, func(t *testing.T) { t.Run(test.Type, func(t *testing.T) {
validator := dt.Validator(test.Type) validator := dt.Build(test.Type)
if validator == nil { if validator == nil {
if test.Handled { if test.Handled {
t.Errorf("expect %q to be handled", test.Type) t.Errorf("expect %q to be handled", test.Type)
@ -50,7 +50,7 @@ func TestUint_Values(t *testing.T) {
const typeName = "uint" const typeName = "uint"
validator := validator.UintType{}.Validator(typeName) validator := builtin.UintDataType{}.Build(typeName)
if validator == nil { if validator == nil {
t.Errorf("expect %q to be handled", typeName) t.Errorf("expect %q to be handled", typeName)
t.Fail() t.Fail()

23
datatype/datatype.go Normal file
View File

@ -0,0 +1,23 @@
package datatype
import (
"reflect"
)
// Validator returns whether a given value fulfills the datatype
// and casts the value into a common go type.
//
// for example, if a validator checks for upper case strings,
// whether the value is a []byte, a string or a []rune, if the
// value matches the validator's checks, it will be cast it into
// a common go type, say, string.
type Validator func(value interface{}) (cast interface{}, valid bool)
// T represents a datatype. The Build function returns a Validator if
// it manages types with the name `typeDefinition` (from the configuration field "type"); else it or returns NIL if the type
// definition does not match this datatype; the registry is passed to allow recursive datatypes (e.g. slices, structs, etc)
// The datatype's validator (when input is valid) must return a cast's go type matching the `Type() reflect.Type`
type T interface {
Type() reflect.Type
Build(typeDefinition string, registry ...T) Validator
}

View File

@ -12,26 +12,26 @@ import (
"github.com/xdrm-io/aicra" "github.com/xdrm-io/aicra"
"github.com/xdrm-io/aicra/api" "github.com/xdrm-io/aicra/api"
"github.com/xdrm-io/aicra/validator" "github.com/xdrm-io/aicra/datatype/builtin"
) )
func addBuiltinTypes(b *aicra.Builder) error { func addBuiltinTypes(b *aicra.Builder) error {
if err := b.Validate(validator.AnyType{}); err != nil { if err := b.AddType(builtin.AnyDataType{}); err != nil {
return err return err
} }
if err := b.Validate(validator.BoolType{}); err != nil { if err := b.AddType(builtin.BoolDataType{}); err != nil {
return err return err
} }
if err := b.Validate(validator.FloatType{}); err != nil { if err := b.AddType(builtin.FloatDataType{}); err != nil {
return err return err
} }
if err := b.Validate(validator.IntType{}); err != nil { if err := b.AddType(builtin.IntDataType{}); err != nil {
return err return err
} }
if err := b.Validate(validator.StringType{}); err != nil { if err := b.AddType(builtin.StringDataType{}); err != nil {
return err return err
} }
if err := b.Validate(validator.UintType{}); err != nil { if err := b.AddType(builtin.UintDataType{}); err != nil {
return err return err
} }
return nil return nil

View File

@ -7,13 +7,13 @@ import (
"net/http" "net/http"
"strings" "strings"
"github.com/xdrm-io/aicra/validator" "github.com/xdrm-io/aicra/datatype"
) )
// Server definition // Server definition
type Server struct { type Server struct {
Validators []validator.Type Types []datatype.T
Services []*Service Services []*Service
} }
// Parse a configuration into a server. Server.Types must be set beforehand to // Parse a configuration into a server. Server.Types must be set beforehand to
@ -32,9 +32,9 @@ func (srv *Server) Parse(r io.Reader) error {
} }
// validate implements the validator interface // validate implements the validator interface
func (server Server) validate(datatypes ...validator.Type) error { func (server Server) validate(datatypes ...datatype.T) error {
for _, service := range server.Services { for _, service := range server.Services {
err := service.validate(server.Validators...) err := service.validate(server.Types...)
if err != nil { if err != nil {
return fmt.Errorf("%s '%s': %w", service.Method, service.Pattern, err) return fmt.Errorf("%s '%s': %w", service.Method, service.Pattern, err)
} }

View File

@ -8,7 +8,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/xdrm-io/aicra/validator" "github.com/xdrm-io/aicra/datatype/builtin"
) )
func TestLegalServiceName(t *testing.T) { func TestLegalServiceName(t *testing.T) {
@ -239,7 +239,7 @@ func TestParamEmptyRenameNoRename(t *testing.T) {
} }
]`) ]`)
srv := &Server{} srv := &Server{}
srv.Validators = append(srv.Validators, validator.AnyType{}) srv.Types = append(srv.Types, builtin.AnyDataType{})
err := srv.Parse(r) err := srv.Parse(r)
if err != nil { if err != nil {
t.Errorf("unexpected error: '%s'", err) t.Errorf("unexpected error: '%s'", err)
@ -275,8 +275,8 @@ func TestOptionalParam(t *testing.T) {
} }
]`) ]`)
srv := &Server{} srv := &Server{}
srv.Validators = append(srv.Validators, validator.AnyType{}) srv.Types = append(srv.Types, builtin.AnyDataType{})
srv.Validators = append(srv.Validators, validator.BoolType{}) srv.Types = append(srv.Types, builtin.BoolDataType{})
err := srv.Parse(r) err := srv.Parse(r)
if err != nil { if err != nil {
t.Errorf("unexpected error: '%s'", err) t.Errorf("unexpected error: '%s'", err)
@ -588,7 +588,7 @@ func TestParseParameters(t *testing.T) {
t.Run(fmt.Sprintf("method.%d", i), func(t *testing.T) { t.Run(fmt.Sprintf("method.%d", i), func(t *testing.T) {
srv := &Server{} srv := &Server{}
srv.Validators = append(srv.Validators, validator.AnyType{}) srv.Types = append(srv.Types, builtin.AnyDataType{})
err := srv.Parse(strings.NewReader(test.Raw)) err := srv.Parse(strings.NewReader(test.Raw))
if err == nil && test.Error != nil { if err == nil && test.Error != nil {
@ -827,8 +827,8 @@ func TestServiceCollision(t *testing.T) {
t.Run(fmt.Sprintf("method.%d", i), func(t *testing.T) { t.Run(fmt.Sprintf("method.%d", i), func(t *testing.T) {
srv := &Server{} srv := &Server{}
srv.Validators = append(srv.Validators, validator.StringType{}) srv.Types = append(srv.Types, builtin.StringDataType{})
srv.Validators = append(srv.Validators, validator.UintType{}) srv.Types = append(srv.Types, builtin.UintDataType{})
err := srv.Parse(strings.NewReader(test.Config)) err := srv.Parse(strings.NewReader(test.Config))
if err == nil && test.Error != nil { if err == nil && test.Error != nil {
@ -997,9 +997,9 @@ func TestMatchSimple(t *testing.T) {
t.Run(fmt.Sprintf("method.%d", i), func(t *testing.T) { t.Run(fmt.Sprintf("method.%d", i), func(t *testing.T) {
srv := &Server{} srv := &Server{}
srv.Validators = append(srv.Validators, validator.AnyType{}) srv.Types = append(srv.Types, builtin.AnyDataType{})
srv.Validators = append(srv.Validators, validator.IntType{}) srv.Types = append(srv.Types, builtin.IntDataType{})
srv.Validators = append(srv.Validators, validator.BoolType{}) srv.Types = append(srv.Types, builtin.BoolDataType{})
err := srv.Parse(strings.NewReader(test.Config)) err := srv.Parse(strings.NewReader(test.Config))
if err != nil { if err != nil {
@ -1081,9 +1081,9 @@ func TestFindPriority(t *testing.T) {
t.Run(fmt.Sprintf("method.%d", i), func(t *testing.T) { t.Run(fmt.Sprintf("method.%d", i), func(t *testing.T) {
srv := &Server{} srv := &Server{}
srv.Validators = append(srv.Validators, validator.AnyType{}) srv.Types = append(srv.Types, builtin.AnyDataType{})
srv.Validators = append(srv.Validators, validator.IntType{}) srv.Types = append(srv.Types, builtin.IntDataType{})
srv.Validators = append(srv.Validators, validator.BoolType{}) srv.Types = append(srv.Types, builtin.BoolDataType{})
err := srv.Parse(strings.NewReader(test.Config)) err := srv.Parse(strings.NewReader(test.Config))
if err != nil { if err != nil {

View File

@ -3,7 +3,7 @@ package config
import ( import (
"reflect" "reflect"
"github.com/xdrm-io/aicra/validator" "github.com/xdrm-io/aicra/datatype"
) )
// Parameter represents a parameter definition (from api.json) // Parameter represents a parameter definition (from api.json)
@ -12,13 +12,13 @@ type Parameter struct {
Type string `json:"type"` Type string `json:"type"`
Rename string `json:"name,omitempty"` Rename string `json:"name,omitempty"`
Optional bool Optional bool
// GoType is the type the Validator will cast into // ExtractType is the type the Validator will cast into
GoType reflect.Type ExtractType reflect.Type
// Validator is inferred from the "type" property // Validator is inferred from the "type" property
Validator validator.ValidateFunc Validator datatype.Validator
} }
func (param *Parameter) validate(datatypes ...validator.Type) error { func (param *Parameter) validate(datatypes ...datatype.T) error {
if len(param.Description) < 1 { if len(param.Description) < 1 {
return errMissingParamDesc return errMissingParamDesc
} }
@ -35,8 +35,8 @@ func (param *Parameter) validate(datatypes ...validator.Type) error {
// find validator // find validator
for _, dtype := range datatypes { for _, dtype := range datatypes {
param.Validator = dtype.Validator(param.Type, datatypes...) param.Validator = dtype.Build(param.Type, datatypes...)
param.GoType = dtype.GoType() param.ExtractType = dtype.Type()
if param.Validator != nil { if param.Validator != nil {
break break
} }

View File

@ -6,7 +6,7 @@ import (
"regexp" "regexp"
"strings" "strings"
"github.com/xdrm-io/aicra/validator" "github.com/xdrm-io/aicra/datatype"
) )
var braceRegex = regexp.MustCompile(`^{([a-z_-]+)}$`) var braceRegex = regexp.MustCompile(`^{([a-z_-]+)}$`)
@ -97,7 +97,7 @@ func (svc *Service) matchPattern(uri string) bool {
} }
// Validate implements the validator interface // Validate implements the validator interface
func (svc *Service) validate(datatypes ...validator.Type) error { func (svc *Service) validate(datatypes ...datatype.T) error {
// check method // check method
err := svc.isMethodAvailable() err := svc.isMethodAvailable()
if err != nil { if err != nil {
@ -195,7 +195,7 @@ func (svc *Service) isPatternValid() error {
return nil return nil
} }
func (svc *Service) validateInput(types []validator.Type) error { func (svc *Service) validateInput(types []datatype.T) error {
// ignore no parameter // ignore no parameter
if svc.Input == nil || len(svc.Input) < 1 { if svc.Input == nil || len(svc.Input) < 1 {
@ -285,7 +285,7 @@ func (svc *Service) validateInput(types []validator.Type) error {
return nil return nil
} }
func (svc *Service) validateOutput(types []validator.Type) error { func (svc *Service) validateOutput(types []datatype.T) error {
// ignore no parameter // ignore no parameter
if svc.Output == nil || len(svc.Output) < 1 { if svc.Output == nil || len(svc.Output) < 1 {

View File

@ -31,17 +31,17 @@ func BuildSignature(service config.Service) *Signature {
} }
// make a pointer if optional // make a pointer if optional
if param.Optional { if param.Optional {
s.Input[param.Rename] = reflect.PtrTo(param.GoType) s.Input[param.Rename] = reflect.PtrTo(param.ExtractType)
continue continue
} }
s.Input[param.Rename] = param.GoType s.Input[param.Rename] = param.ExtractType
} }
for _, param := range service.Output { for _, param := range service.Output {
if len(param.Rename) < 1 { if len(param.Rename) < 1 {
continue continue
} }
s.Output[param.Rename] = param.GoType s.Output[param.Rename] = param.ExtractType
} }
return s return s

View File

@ -1,24 +0,0 @@
package validator
import (
"reflect"
)
// AnyType makes the "any" type available in the aicra configuration
// It considers valid any value
type AnyType struct{}
// GoType returns the interface{} type
func (AnyType) GoType() reflect.Type {
return reflect.TypeOf(interface{}(nil))
}
// Validator that considers any value valid
func (AnyType) Validator(typename string, avail ...Type) ValidateFunc {
if typename != "any" {
return nil
}
return func(value interface{}) (interface{}, bool) {
return value, true
}
}

View File

@ -1,55 +0,0 @@
package validator
import (
"reflect"
)
// ValidateFunc returns whether a given value fulfills the datatype and casts
// the value into a go type.
//
// for example, if a validator checks for upper case strings, whether the value
// is a []byte, a string or a []rune, if the value matches is all upper-case, it
// will be cast into a go type, say, string.
type ValidateFunc func(value interface{}) (cast interface{}, valid bool)
// Type defines an available in/out parameter "type" for the aicra configuration
//
// A Type maps to a go type in order to generate the handler signature from the
// aicra configuration
//
// A Type returns a custom validator when the typename matches
type Type interface {
// Validator function when the typename matches. It must return nil when the
// typename does not match
//
// The `typename` argument has to match types used in your aicra configuration
// in parameter definitions ("in", "out") and in the "type" json field.
//
// basic example:
// - `IntType.Validator("string")`` should return nil
// - `IntType.Validator("int")`` should return its ValidateFunc
//
// The `typename` is not returned by a simple method i.e. `TypeName() string`
// because it allows for validation relative to the typename, for instance:
// - `VarcharType.Validator("varchar")` valides any string
// - `VarcharType.Validator("varchar(2)")` validates any string of 2
// characters
// - `VarcharType.Validator("varchar(1,3)")` validates any string
// with a length between 1 and 3
//
// The `avail` argument represents all other available Types. It allows a
// Type to use other available Types internally.
//
// recursive example: slices
// - `SliceType.Validator("[]int", avail...)` validates a slice containing
// values that are valide to the `IntType`
// - `SliceType.Validator("[]varchar", avail...)` validates a slice containing
// values that are valid to the `VarcharType`
//
// and so on.. this works for maps, structs, etc
Validator(typename string, avail ...Type) ValidateFunc
// GoType must return the go type associated with the output type of ValidateFunc.
// It is used to define handlers' signature from the configuration file.
GoType() reflect.Type
}