1
0
mirror of https://github.com/ggicci/httpin.git synced 2024-11-30 08:56:52 +02:00

feat: debug switch, default UTC time and up tests

This commit is contained in:
Ggicci 2021-05-06 16:06:55 +08:00
parent 3ccdd2a4f9
commit 5be2274565
7 changed files with 54 additions and 25 deletions

View File

@ -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.

View File

@ -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
View 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...)
}
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
})
}

View File

@ -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,