remove Parameter type, only keep method parseParameter()
This commit is contained in:
parent
149ec9a9a0
commit
9a5b0dd6e3
|
@ -1,95 +0,0 @@
|
|||
package reqdata
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Parameter represents an http request parameter
|
||||
// that can be of type URL, GET, or FORM (multipart, json, urlencoded)
|
||||
type Parameter struct {
|
||||
// whether the value has been json-parsed
|
||||
// for optimisation purpose, parameters are only parsed
|
||||
// if they are required by the current service
|
||||
Parsed bool
|
||||
|
||||
// whether the value is a file
|
||||
File bool
|
||||
|
||||
// the actual parameter value
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// Parse parameter (json-like) if not already done
|
||||
func (i *Parameter) Parse() {
|
||||
|
||||
/* ignore already parsed or nil*/
|
||||
if i.Parsed || i.Value == nil {
|
||||
return
|
||||
}
|
||||
|
||||
/* parse value */
|
||||
i.Parsed = true
|
||||
i.Value = parseParameter(i.Value)
|
||||
}
|
||||
|
||||
// parseParameter parses http URI/GET/POST data
|
||||
// - []string : return array of json elements
|
||||
// - string : return json if valid, else return raw string
|
||||
func parseParameter(data interface{}) interface{} {
|
||||
dtype := reflect.TypeOf(data)
|
||||
dvalue := reflect.ValueOf(data)
|
||||
|
||||
switch dtype.Kind() {
|
||||
|
||||
/* (1) []string -> recursive */
|
||||
case reflect.Slice:
|
||||
|
||||
// 1. ignore empty
|
||||
if dvalue.Len() == 0 {
|
||||
return data
|
||||
}
|
||||
|
||||
// 2. parse each element recursively
|
||||
result := make([]interface{}, dvalue.Len())
|
||||
|
||||
for i, l := 0, dvalue.Len(); i < l; i++ {
|
||||
element := dvalue.Index(i)
|
||||
result[i] = parseParameter(element.Interface())
|
||||
}
|
||||
return result
|
||||
|
||||
/* (2) string -> parse */
|
||||
case reflect.String:
|
||||
|
||||
// build json wrapper
|
||||
wrapper := fmt.Sprintf("{\"wrapped\":%s}", dvalue.String())
|
||||
|
||||
// try to parse as json
|
||||
var result interface{}
|
||||
err := json.Unmarshal([]byte(wrapper), &result)
|
||||
|
||||
// return if success
|
||||
if err != nil {
|
||||
return dvalue.String()
|
||||
}
|
||||
|
||||
mapval, ok := result.(map[string]interface{})
|
||||
if !ok {
|
||||
return dvalue.String()
|
||||
}
|
||||
|
||||
wrapped, ok := mapval["wrapped"]
|
||||
if !ok {
|
||||
return dvalue.String()
|
||||
}
|
||||
|
||||
return wrapped
|
||||
|
||||
}
|
||||
|
||||
/* (3) NIL if unknown type */
|
||||
return dvalue.Interface()
|
||||
|
||||
}
|
|
@ -6,15 +6,9 @@ import (
|
|||
)
|
||||
|
||||
func TestSimpleString(t *testing.T) {
|
||||
p := Parameter{Value: "some-string"}
|
||||
p.Parse()
|
||||
p := parseParameter("some-string")
|
||||
|
||||
if !p.Parsed {
|
||||
t.Errorf("expected parameter to be parsed")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
cast, canCast := p.Value.(string)
|
||||
cast, canCast := p.(string)
|
||||
if !canCast {
|
||||
t.Errorf("expected parameter to be a string")
|
||||
t.FailNow()
|
||||
|
@ -31,15 +25,9 @@ func TestSimpleFloat(t *testing.T) {
|
|||
|
||||
for i, tcase := range tcases {
|
||||
t.Run("case "+string(i), func(t *testing.T) {
|
||||
p := Parameter{Parsed: false, File: false, Value: tcase}
|
||||
p.Parse()
|
||||
p := parseParameter(tcase)
|
||||
|
||||
if !p.Parsed {
|
||||
t.Errorf("expected parameter to be parsed")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
cast, canCast := p.Value.(float64)
|
||||
cast, canCast := p.(float64)
|
||||
if !canCast {
|
||||
t.Errorf("expected parameter to be a float64")
|
||||
t.FailNow()
|
||||
|
@ -58,16 +46,9 @@ func TestSimpleBool(t *testing.T) {
|
|||
|
||||
for i, tcase := range tcases {
|
||||
t.Run("case "+string(i), func(t *testing.T) {
|
||||
p := Parameter{Parsed: false, File: false, Value: tcase}
|
||||
p := parseParameter(tcase)
|
||||
|
||||
p.Parse()
|
||||
|
||||
if !p.Parsed {
|
||||
t.Errorf("expected parameter to be parsed")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
cast, canCast := p.Value.(bool)
|
||||
cast, canCast := p.(bool)
|
||||
if !canCast {
|
||||
t.Errorf("expected parameter to be a bool")
|
||||
t.FailNow()
|
||||
|
@ -82,15 +63,9 @@ func TestSimpleBool(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestJsonStringSlice(t *testing.T) {
|
||||
p := Parameter{Parsed: false, File: false, Value: `["str1", "str2"]`}
|
||||
p.Parse()
|
||||
p := parseParameter(`["str1", "str2"]`)
|
||||
|
||||
if !p.Parsed {
|
||||
t.Errorf("expected parameter to be parsed")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
slice, canCast := p.Value.([]interface{})
|
||||
slice, canCast := p.([]interface{})
|
||||
if !canCast {
|
||||
t.Errorf("expected parameter to be a []interface{}")
|
||||
t.FailNow()
|
||||
|
@ -120,15 +95,9 @@ func TestJsonStringSlice(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestStringSlice(t *testing.T) {
|
||||
p := Parameter{Parsed: false, File: false, Value: []string{"str1", "str2"}}
|
||||
p.Parse()
|
||||
p := parseParameter([]string{"str1", "str2"})
|
||||
|
||||
if !p.Parsed {
|
||||
t.Errorf("expected parameter to be parsed")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
slice, canCast := p.Value.([]interface{})
|
||||
slice, canCast := p.([]interface{})
|
||||
if !canCast {
|
||||
t.Errorf("expected parameter to be a []interface{}")
|
||||
t.FailNow()
|
||||
|
@ -168,15 +137,9 @@ func TestJsonPrimitiveBool(t *testing.T) {
|
|||
|
||||
for i, tcase := range tcases {
|
||||
t.Run("case "+string(i), func(t *testing.T) {
|
||||
p := Parameter{Parsed: false, File: false, Value: tcase.Raw}
|
||||
p.Parse()
|
||||
p := parseParameter(tcase.Raw)
|
||||
|
||||
if !p.Parsed {
|
||||
t.Errorf("expected parameter to be parsed")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
cast, canCast := p.Value.(bool)
|
||||
cast, canCast := p.(bool)
|
||||
if !canCast {
|
||||
t.Errorf("expected parameter to be a bool")
|
||||
t.FailNow()
|
||||
|
@ -211,15 +174,9 @@ func TestJsonPrimitiveFloat(t *testing.T) {
|
|||
|
||||
for i, tcase := range tcases {
|
||||
t.Run("case "+string(i), func(t *testing.T) {
|
||||
p := Parameter{Parsed: false, File: false, Value: tcase.Raw}
|
||||
p.Parse()
|
||||
p := parseParameter(tcase.Raw)
|
||||
|
||||
if !p.Parsed {
|
||||
t.Errorf("expected parameter to be parsed")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
cast, canCast := p.Value.(float64)
|
||||
cast, canCast := p.(float64)
|
||||
if !canCast {
|
||||
t.Errorf("expected parameter to be a float64")
|
||||
t.FailNow()
|
||||
|
@ -235,15 +192,9 @@ func TestJsonPrimitiveFloat(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestJsonBoolSlice(t *testing.T) {
|
||||
p := Parameter{Parsed: false, File: false, Value: []string{"true", "false"}}
|
||||
p.Parse()
|
||||
p := parseParameter([]string{"true", "false"})
|
||||
|
||||
if !p.Parsed {
|
||||
t.Errorf("expected parameter to be parsed")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
slice, canCast := p.Value.([]interface{})
|
||||
slice, canCast := p.([]interface{})
|
||||
if !canCast {
|
||||
t.Errorf("expected parameter to be a []interface{}")
|
||||
t.FailNow()
|
||||
|
@ -273,15 +224,9 @@ func TestJsonBoolSlice(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestBoolSlice(t *testing.T) {
|
||||
p := Parameter{Parsed: false, File: false, Value: []bool{true, false}}
|
||||
p.Parse()
|
||||
p := parseParameter([]bool{true, false})
|
||||
|
||||
if !p.Parsed {
|
||||
t.Errorf("expected parameter to be parsed")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
slice, canCast := p.Value.([]interface{})
|
||||
slice, canCast := p.([]interface{})
|
||||
if !canCast {
|
||||
t.Errorf("expected parameter to be a []interface{}")
|
||||
t.FailNow()
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
|
||||
"git.xdrm.io/go/aicra/internal/config"
|
||||
"git.xdrm.io/go/aicra/internal/multipart"
|
||||
|
@ -26,14 +27,14 @@ type Set struct {
|
|||
// - FORM: no prefix
|
||||
// - URL: '{uri_var}'
|
||||
// - GET: 'GET@' followed by the key in GET
|
||||
Data map[string]*Parameter
|
||||
Data map[string]interface{}
|
||||
}
|
||||
|
||||
// New creates a new empty store.
|
||||
func New(service *config.Service) *Set {
|
||||
return &Set{
|
||||
service: service,
|
||||
Data: make(map[string]*Parameter),
|
||||
Data: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,17 +54,17 @@ func (i *Set) ExtractURI(req *http.Request) error {
|
|||
return fmt.Errorf("%s: %w", capture.Name, ErrUnknownType)
|
||||
}
|
||||
|
||||
// parse parameter
|
||||
parsed := parseParameter(value)
|
||||
|
||||
// check type
|
||||
cast, valid := capture.Ref.Validator(value)
|
||||
cast, valid := capture.Ref.Validator(parsed)
|
||||
if !valid {
|
||||
return fmt.Errorf("%s: %w", capture.Name, ErrInvalidType)
|
||||
}
|
||||
|
||||
// store cast value in 'Set'
|
||||
i.Data[capture.Ref.Rename] = &Parameter{
|
||||
Value: cast,
|
||||
}
|
||||
|
||||
i.Data[capture.Ref.Rename] = cast
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -86,16 +87,17 @@ func (i *Set) ExtractQuery(req *http.Request) error {
|
|||
continue
|
||||
}
|
||||
|
||||
// parse parameter
|
||||
parsed := parseParameter(value)
|
||||
|
||||
// check type
|
||||
cast, valid := param.Validator(value)
|
||||
cast, valid := param.Validator(parsed)
|
||||
if !valid {
|
||||
return fmt.Errorf("%s: %w", name, ErrInvalidType)
|
||||
}
|
||||
|
||||
// store value
|
||||
i.Data[param.Rename] = &Parameter{
|
||||
Value: cast,
|
||||
}
|
||||
// store cast value
|
||||
i.Data[param.Rename] = cast
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -167,10 +169,8 @@ func (i *Set) parseJSON(req *http.Request) error {
|
|||
return fmt.Errorf("%s: %w", name, ErrInvalidType)
|
||||
}
|
||||
|
||||
// store value
|
||||
i.Data[param.Rename] = &Parameter{
|
||||
Value: cast,
|
||||
}
|
||||
// store cast value
|
||||
i.Data[param.Rename] = cast
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -197,16 +197,17 @@ func (i *Set) parseUrlencoded(req *http.Request) error {
|
|||
continue
|
||||
}
|
||||
|
||||
// parse parameter
|
||||
parsed := parseParameter(value)
|
||||
|
||||
// check type
|
||||
cast, valid := param.Validator(value)
|
||||
cast, valid := param.Validator(parsed)
|
||||
if !valid {
|
||||
return fmt.Errorf("%s: %w", name, ErrInvalidType)
|
||||
}
|
||||
|
||||
// store value
|
||||
i.Data[param.Rename] = &Parameter{
|
||||
Value: cast,
|
||||
}
|
||||
// store cast value
|
||||
i.Data[param.Rename] = cast
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -244,18 +245,79 @@ func (i *Set) parseMultipart(req *http.Request) error {
|
|||
continue
|
||||
}
|
||||
|
||||
// parse parameter
|
||||
parsed := parseParameter(string(component.Data))
|
||||
|
||||
// fail on invalid type
|
||||
cast, valid := param.Validator(string(component.Data))
|
||||
cast, valid := param.Validator(parsed)
|
||||
if !valid {
|
||||
return fmt.Errorf("%s: %w", name, ErrInvalidType)
|
||||
}
|
||||
|
||||
// store value
|
||||
i.Data[param.Rename] = &Parameter{
|
||||
Value: cast,
|
||||
}
|
||||
// store cast value
|
||||
i.Data[param.Rename] = cast
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// parseParameter parses http URI/GET/POST data
|
||||
// - []string : return array of json elements
|
||||
// - string : return json if valid, else return raw string
|
||||
func parseParameter(data interface{}) interface{} {
|
||||
dtype := reflect.TypeOf(data)
|
||||
dvalue := reflect.ValueOf(data)
|
||||
|
||||
switch dtype.Kind() {
|
||||
|
||||
/* (1) []string -> recursive */
|
||||
case reflect.Slice:
|
||||
|
||||
// 1. ignore empty
|
||||
if dvalue.Len() == 0 {
|
||||
return data
|
||||
}
|
||||
|
||||
// 2. parse each element recursively
|
||||
result := make([]interface{}, dvalue.Len())
|
||||
|
||||
for i, l := 0, dvalue.Len(); i < l; i++ {
|
||||
element := dvalue.Index(i)
|
||||
result[i] = parseParameter(element.Interface())
|
||||
}
|
||||
return result
|
||||
|
||||
/* (2) string -> parse */
|
||||
case reflect.String:
|
||||
|
||||
// build json wrapper
|
||||
wrapper := fmt.Sprintf("{\"wrapped\":%s}", dvalue.String())
|
||||
|
||||
// try to parse as json
|
||||
var result interface{}
|
||||
err := json.Unmarshal([]byte(wrapper), &result)
|
||||
|
||||
// return if success
|
||||
if err != nil {
|
||||
return dvalue.String()
|
||||
}
|
||||
|
||||
mapval, ok := result.(map[string]interface{})
|
||||
if !ok {
|
||||
return dvalue.String()
|
||||
}
|
||||
|
||||
wrapped, ok := mapval["wrapped"]
|
||||
if !ok {
|
||||
return dvalue.String()
|
||||
}
|
||||
|
||||
return wrapped
|
||||
|
||||
}
|
||||
|
||||
/* (3) NIL if unknown type */
|
||||
return dvalue.Interface()
|
||||
|
||||
}
|
||||
|
|
|
@ -280,7 +280,7 @@ func TestExtractQuery(t *testing.T) {
|
|||
t.FailNow()
|
||||
}
|
||||
|
||||
cast, canCast := param.Value.([]string)
|
||||
cast, canCast := param.([]interface{})
|
||||
if !canCast {
|
||||
t.Errorf("should return a []string (got '%v')", cast)
|
||||
t.FailNow()
|
||||
|
@ -458,9 +458,9 @@ func TestExtractFormUrlEncoded(t *testing.T) {
|
|||
t.FailNow()
|
||||
}
|
||||
|
||||
cast, canCast := param.Value.([]string)
|
||||
cast, canCast := param.([]interface{})
|
||||
if !canCast {
|
||||
t.Errorf("should return a []string (got '%v')", cast)
|
||||
t.Errorf("should return a []interface{} (got '%v')", cast)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
|
@ -606,8 +606,8 @@ func TestJsonParameters(t *testing.T) {
|
|||
|
||||
valueType := reflect.TypeOf(value)
|
||||
|
||||
paramValue := param.Value
|
||||
paramValueType := reflect.TypeOf(param.Value)
|
||||
paramValue := param
|
||||
paramValueType := reflect.TypeOf(param)
|
||||
|
||||
if valueType != paramValueType {
|
||||
t.Errorf("should be of type %v (got '%v')", valueType, paramValueType)
|
||||
|
@ -762,8 +762,8 @@ x
|
|||
|
||||
valueType := reflect.TypeOf(value)
|
||||
|
||||
paramValue := param.Value
|
||||
paramValueType := reflect.TypeOf(param.Value)
|
||||
paramValue := param
|
||||
paramValueType := reflect.TypeOf(param)
|
||||
|
||||
if valueType != paramValueType {
|
||||
t.Errorf("should be of type %v (got '%v')", valueType, paramValueType)
|
||||
|
|
Loading…
Reference in New Issue