mirror of
https://github.com/ggicci/httpin.git
synced 2024-11-28 08:49:05 +02:00
feat: debug switch, default UTC time and up tests
This commit is contained in:
parent
3ccdd2a4f9
commit
5be2274565
14
README.md
14
README.md
@ -14,18 +14,18 @@ Suppose that we have an RESTful API for querying users. We can define a struct t
|
||||
|
||||
```go
|
||||
type Authorization struct {
|
||||
Token string `in:"query.access_token,header.x-api-token"`
|
||||
Token string `in:"form=access_token,header=x-api-token"`
|
||||
}
|
||||
|
||||
type Pagination struct {
|
||||
Page int `in:"query.page"`
|
||||
PerPage int `in:"query.per_page"`
|
||||
Page int `in:"form=page"`
|
||||
PerPage int `in:"form=per_page,page_size"`
|
||||
}
|
||||
|
||||
type UserQuery struct {
|
||||
Gender string `in:"query.gender"`
|
||||
AgeRange []int `in:"query.age_range"`
|
||||
IsMember bool `in:"query.is_member"`
|
||||
Gender string `in:"form=gender"`
|
||||
AgeRange []int `in:"form=age_range"`
|
||||
IsMember bool `in:"form=is_member"`
|
||||
Pagination
|
||||
Authorization
|
||||
}
|
||||
@ -59,7 +59,7 @@ func QueryUser(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
```
|
||||
|
||||
## Advanced Usage - Use Middleware
|
||||
## Advanced: Use Middleware
|
||||
|
||||
Firstly setup httpin middleware for you APIs.
|
||||
|
||||
|
@ -104,13 +104,13 @@ func DecodeString(data []byte, rv reflect.Value) error {
|
||||
func parseTime(value string) (time.Time, error) {
|
||||
// Try parsing value as RFC3339 format.
|
||||
if t, err := time.Parse(time.RFC3339Nano, value); err == nil {
|
||||
return t, nil
|
||||
return t.UTC(), nil
|
||||
}
|
||||
|
||||
// Try parsing value as int64 (timestamp).
|
||||
// TODO(ggicci): can support float timestamp, e.g. 1618974933.284368
|
||||
if timestamp, err := strconv.ParseInt(value, 10, 64); err == nil {
|
||||
return time.Unix(timestamp, 0), nil
|
||||
return time.Unix(timestamp, 0).UTC(), nil
|
||||
}
|
||||
|
||||
return time.Time{}, fmt.Errorf("invalid time value, use time.RFC3339Nano format or timestamp")
|
||||
|
23
debug.go
Normal file
23
debug.go
Normal file
@ -0,0 +1,23 @@
|
||||
package httpin
|
||||
|
||||
import (
|
||||
"log"
|
||||
)
|
||||
|
||||
var (
|
||||
debugOn = false
|
||||
)
|
||||
|
||||
func DebugOn() {
|
||||
debugOn = true
|
||||
}
|
||||
|
||||
func DebugOff() {
|
||||
debugOn = false
|
||||
}
|
||||
|
||||
func debug(format string, v ...interface{}) {
|
||||
if debugOn {
|
||||
log.Printf(format, v...)
|
||||
}
|
||||
}
|
@ -63,7 +63,7 @@ func RegisterDirectiveExecutor(name string, exe DirectiveExecutor) {
|
||||
panic(fmt.Sprintf("duplicate executor: %q", name))
|
||||
}
|
||||
executors[name] = exe
|
||||
fmt.Printf("directive executor registered: %q\n", name)
|
||||
debug("directive executor registered: %q\n", name)
|
||||
}
|
||||
|
||||
func buildDirective(directive string) (*Directive, error) {
|
||||
@ -100,7 +100,7 @@ func extractFromKVSWithKeyForSlice(ctx *DirectiveContext, kvs map[string][]strin
|
||||
|
||||
formValues, exists := kvs[key]
|
||||
if !exists {
|
||||
fmt.Printf(" > key %q not found in %s\n", key, ctx.Executor)
|
||||
debug(" > key %q not found in %s\n", key, ctx.Executor)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -118,7 +118,7 @@ func extractFromKVSWithKeyForSlice(ctx *DirectiveContext, kvs map[string][]strin
|
||||
|
||||
func extractFromKVSWithKey(ctx *DirectiveContext, kvs map[string][]string, key string) error {
|
||||
if ctx.Context.Value(fieldSet) == true {
|
||||
fmt.Printf(" > field already set, skip\n")
|
||||
debug(" > field already set, skip\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -133,7 +133,7 @@ func extractFromKVSWithKey(ctx *DirectiveContext, kvs map[string][]string, key s
|
||||
|
||||
formValues, exists := kvs[key]
|
||||
if !exists {
|
||||
fmt.Printf(" > key %q not found in %s\n", key, ctx.Executor)
|
||||
debug(" > key %q not found in %s\n", key, ctx.Executor)
|
||||
return nil
|
||||
}
|
||||
var got string
|
||||
@ -159,7 +159,7 @@ func extractFromKVSWithKey(ctx *DirectiveContext, kvs map[string][]string, key s
|
||||
|
||||
func extractFromKVS(ctx *DirectiveContext, kvs map[string][]string, headerKey bool) error {
|
||||
for _, key := range ctx.Directive.Argv {
|
||||
fmt.Printf(" > execute directive %q with key %q\n", ctx.Directive.Executor, key)
|
||||
debug(" > execute directive %q with key %q\n", ctx.Directive.Executor, key)
|
||||
if headerKey {
|
||||
key = http.CanonicalHeaderKey(key)
|
||||
}
|
||||
|
11
errors.go
11
errors.go
@ -6,7 +6,10 @@ import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var ErrMissingField = errors.New("field required but missing")
|
||||
var (
|
||||
ErrMissingField = errors.New("field required but missing")
|
||||
ErrUnsupporetedType = errors.New("unsupported type")
|
||||
)
|
||||
|
||||
type UnsupportedTypeError struct {
|
||||
Type reflect.Type
|
||||
@ -17,6 +20,10 @@ func (e UnsupportedTypeError) Error() string {
|
||||
return fmt.Sprintf("httpin: unsupported type in %s: %s", e.Where, e.Type.String())
|
||||
}
|
||||
|
||||
func (e UnsupportedTypeError) Unwrap() error {
|
||||
return ErrUnsupporetedType
|
||||
}
|
||||
|
||||
type InvalidField struct {
|
||||
// Field is the name of the field.
|
||||
Field string `json:"field"`
|
||||
@ -36,6 +43,6 @@ func (f *InvalidField) Error() string {
|
||||
return fmt.Sprintf("httpin: invalid field %q: %v", f.Field, f.InternalError)
|
||||
}
|
||||
|
||||
func (f *InvalidField) Unwarp() error {
|
||||
func (f *InvalidField) Unwrap() error {
|
||||
return f.InternalError
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -134,14 +133,14 @@ func TestCore(t *testing.T) {
|
||||
Complex64Value: 1 + 4i,
|
||||
Complex128Value: -6 + 17i,
|
||||
StringValue: "doggy",
|
||||
TimeValue: time.Date(1991, 11, 10, 8, 0, 0, 0, time.FixedZone("E8", 8*3600)),
|
||||
TimeValue: time.Date(1991, 11, 10, 0, 0, 0, 0, time.UTC),
|
||||
BoolList: []bool{true, false, false, true},
|
||||
IntList: []int{9, 9, 6},
|
||||
FloatList: []float64{0.0, 0.5, 1.0},
|
||||
StringList: []string{"Life", "is", "a", "Miracle"},
|
||||
TimeList: []time.Time{
|
||||
time.Date(2000, 1, 2, 15, 4, 5, 0, time.FixedZone("W7", -7*3600)),
|
||||
time.Date(1991, 6, 28, 14, 0, 0, 0, time.FixedZone("E8", 8*3600)),
|
||||
time.Date(2000, 1, 2, 22, 4, 5, 0, time.UTC),
|
||||
time.Date(1991, 6, 28, 6, 0, 0, 0, time.UTC),
|
||||
},
|
||||
}
|
||||
|
||||
@ -164,11 +163,11 @@ func TestCore(t *testing.T) {
|
||||
"per_page": {"20"},
|
||||
}
|
||||
expected := &ProductQuery{
|
||||
CreatedAt: time.Date(1991, 11, 10, 8, 0, 0, 0, time.FixedZone("+08:00", 8*3600)),
|
||||
CreatedAt: time.Date(1991, 11, 10, 0, 0, 0, 0, time.UTC),
|
||||
Color: "red",
|
||||
IsSoldout: true,
|
||||
SortBy: []string{"id", "quantity"},
|
||||
SortDesc: []bool{true, true},
|
||||
SortDesc: []bool{false, true},
|
||||
Pagination: Pagination{
|
||||
Page: 1,
|
||||
PerPage: 20,
|
||||
@ -211,7 +210,7 @@ func TestCore(t *testing.T) {
|
||||
got, err := core.ReadRequest(r)
|
||||
So(got, ShouldBeNil)
|
||||
So(err, ShouldBeError)
|
||||
So(errors.Is(err, UnsupportedTypeError{Type: reflect.TypeOf(ObjectID{})}), ShouldBeTrue)
|
||||
So(errors.Is(err, ErrUnsupporetedType), ShouldBeTrue)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ func (r *FieldResolver) IsRoot() bool {
|
||||
|
||||
func (r *FieldResolver) resolve(req *http.Request) (reflect.Value, error) {
|
||||
rv := reflect.New(r.Type)
|
||||
fmt.Printf("resolve: %s (of %s)\n", r.Field.Name, r.Type)
|
||||
debug("resolve: %s (of %s)\n", r.Field.Name, r.Type)
|
||||
|
||||
// Execute directives.
|
||||
if len(r.Directives) > 0 {
|
||||
@ -35,7 +35,7 @@ func (r *FieldResolver) resolve(req *http.Request) (reflect.Value, error) {
|
||||
Value: rv,
|
||||
Context: inheritableContext,
|
||||
}
|
||||
fmt.Printf(" > execute directive: %s with %v\n", dir.Executor, dir.Argv)
|
||||
debug(" > execute directive: %s with %v\n", dir.Executor, dir.Argv)
|
||||
if err := dir.Execute(directiveContext); err != nil {
|
||||
return rv, &InvalidField{
|
||||
Field: r.Field.Name,
|
||||
|
Loading…
Reference in New Issue
Block a user