feat: support exception fields in BinaryConv t2j (#21)

* support exception field

* delete unused fields

* delete unused file

* optimize

* return json string

* add comments

* fix comment
This commit is contained in:
Marina Sakai 2023-04-21 12:08:41 +08:00 committed by GitHub
parent e68c1c1068
commit 962f7eaad2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 65 additions and 18 deletions

View File

@ -48,18 +48,18 @@ var (
)
type Options struct {
// EnableValueMapping indicates if value mapping (api.js_conv...) should be enabled
EnableValueMapping bool
// EnableHttpMapping indicates if http mapping (api.query|api.header...) should be enabled
EnableHttpMapping bool
// EnableThriftBase indicates if thrift/base should be recoginized and mapping to/from context
// EnableThriftBase indicates if thrift/base should be recognized and mapping to/from context
EnableThriftBase bool
// Int64AsString indicates if string value cane be read as **Int8/Int16/Int32/Int64/Float64**,
// or in response a **Int64** value can be written as string
String2Int64 bool
// NoBase64Binary indicates if base64 string shoud be Encode/Decode as []byte
// NoBase64Binary indicates if base64 string should be Encode/Decode as []byte
NoBase64Binary bool
// ByteAsUint8 indicates if byte should be conv as uint8 (default is int8), this only works for t2j now
ByteAsUint8 bool
@ -73,7 +73,7 @@ type Options struct {
WriteRequireField bool
// DisallowUnknownField indicates if unknown fields should be skipped
DisallowUnknownField bool
// ReadHttpValueFallback indicates if http-annotated fields should fallback to http body after reading from non-body parts (header,cookie...) failed
ReadHttpValueFallback bool
// WriteHttpValueFallback indicates if http-annotated fields should fallback to http body after writing to non-body parts (header,cookie...) failed
@ -82,15 +82,17 @@ type Options struct {
// or root-level fields should be seeking on http-values when reading failed from current layer of json.
// this option is only used in j2t now.
TracebackRequredOrRootFields bool
// OmitHttpMappingErrors indicates to omit http-mapping failing errors.
// OmitHttpMappingErrors indicates to omit http-mapping failing errors.
// If there are more-than-one HTTP annotations on the field, dynamicgo will try to mapping next annotation source (from left to right) until succeed.
OmitHttpMappingErrors bool
// NoCopyString indicates if string-kind http values should be copied or just referenced (if possible)
NoCopyString bool
// UseNativeSkip indicates if use thrift.SkipNative() or thrift.SkipGo()
// UseNativeSkip indicates if using thrift.SkipNative() or thrift.SkipGo()
UseNativeSkip bool
// ConvertException indicates that it returns error for thrift exception fields when doing BinaryConv t2j
ConvertException bool
}
var bufPool = sync.Pool{

View File

@ -282,6 +282,28 @@ func TestAGWBodyDynamic(t *testing.T) {
require.Equal(t, (`{"Int64":1,"Xjson":"{\"b\":1}"}`), string(out))
}
func TestException(t *testing.T) {
cv := NewBinaryConv(conv.Options{
ConvertException: true,
})
desc := thrift.FnWholeResponse(thrift.GetFnDescFromFile("testdata/idl/example3.thrift", "ExampleMethod", thrift.Options{}))
exp := example3.NewExampleServiceExampleMethodResult()
success := example3.NewExampleResp()
success.Status = 202
exception := example3.NewException()
exception.Code = 400
exception.Msg = "this is an exception"
exp.Success = success
exp.Err = exception
ctx := context.Background()
in := make([]byte, exp.BLength())
_ = exp.FastWriteNocopy(in, nil)
_, err := cv.Do(ctx, desc, in)
require.Error(t, err)
require.Equal(t, err.Error(), `{"code":400,"msg":"this is an exception"}`)
}
func TestInt2String(t *testing.T) {
cv := NewBinaryConv(conv.Options{
EnableValueMapping: true,
@ -323,9 +345,9 @@ func TestHttpMappingFallback(t *testing.T) {
_ = exp.FastWriteNocopy(in, nil)
expJSON := `{}`
cv.SetOptions(conv.Options{
EnableHttpMapping: true,
EnableHttpMapping: true,
WriteHttpValueFallback: false,
OmitHttpMappingErrors: true,
OmitHttpMappingErrors: true,
})
ctx := context.Background()
resp := http.NewHTTPResponse()
@ -346,9 +368,9 @@ func TestHttpMappingFallback(t *testing.T) {
_ = exp.FastWriteNocopy(in, nil)
expJSON := `{"Msg":"hello"}`
cv.SetOptions(conv.Options{
EnableHttpMapping: true,
EnableHttpMapping: true,
WriteHttpValueFallback: true,
OmitHttpMappingErrors: true,
OmitHttpMappingErrors: true,
})
ctx := context.Background()
resp := http.NewHTTPResponse()
@ -510,9 +532,9 @@ func TestJSONString(t *testing.T) {
_ = exp.FastWriteNocopy(in, nil)
cv := NewBinaryConv(conv.Options{
EnableHttpMapping: true,
EnableHttpMapping: true,
WriteHttpValueFallback: true,
OmitHttpMappingErrors: true,
OmitHttpMappingErrors: true,
})
ctx := context.Background()
resp := http.NewHTTPResponse()
@ -635,7 +657,6 @@ func TestOptionalDefaultValue(t *testing.T) {
})
}
func TestSimpleArgs(t *testing.T) {
cv := NewBinaryConv(conv.Options{})
@ -658,4 +679,4 @@ func TestSimpleArgs(t *testing.T) {
require.NoError(t, err)
require.Equal(t, strconv.Itoa(math.MaxInt64), string(out))
})
}
}

View File

@ -18,6 +18,7 @@ package t2j
import (
"context"
"errors"
"fmt"
"github.com/cloudwego/dynamicgo/conv"
@ -83,6 +84,8 @@ func (self *BinaryConv) do(ctx context.Context, src []byte, desc *thrift.TypeDes
desc.Struct().Requires().CopyTo(r)
comma := false
existExceptionField := false
for {
_, typeId, id, e := p.ReadFieldBegin()
if e != nil {
@ -139,6 +142,13 @@ func (self *BinaryConv) do(ctx context.Context, src []byte, desc *thrift.TypeDes
*out = json.EncodeString(*out, field.Alias())
*out = json.EncodeObjectColon(*out)
// handle a thrift exception field. the id of a thrift exception field is non-zero
if self.opts.ConvertException && id != 0 {
existExceptionField = true
// reset out to get only exception field data
*out = (*out)[:0]
}
if self.opts.EnableValueMapping && field.ValueMapping() != nil {
err = field.ValueMapping().Read(ctx, &p, field, out)
if err != nil {
@ -150,6 +160,10 @@ func (self *BinaryConv) do(ctx context.Context, src []byte, desc *thrift.TypeDes
return unwrapError(fmt.Sprintf("converting field %s of STRUCT %s failed", field.Name(), desc.Type()), err)
}
}
if existExceptionField {
break
}
}
if err = self.handleUnsets(r, desc.Struct(), out, comma, ctx, resp); err != nil {
@ -157,7 +171,11 @@ func (self *BinaryConv) do(ctx context.Context, src []byte, desc *thrift.TypeDes
}
thrift.FreeRequiresBitmap(r)
*out = json.EncodeObjectEnd(*out)
if existExceptionField && err == nil {
err = errors.New(string(*out))
} else {
*out = json.EncodeObjectEnd(*out)
}
return err
}

View File

@ -43,6 +43,12 @@ func FnResponse(fn *FunctionDescriptor) *TypeDescriptor {
return fn.Response().Struct().FieldById(0).Type()
}
// FnWholeResponse get the normal response type
func FnWholeResponse(fn *FunctionDescriptor) *TypeDescriptor {
// let-it-fail: it panic when something is nil
return fn.Response()
}
// FnRequest
// We assume the request only have one argument and the only argument it the type we want.
func FnRequest(fn *FunctionDescriptor) *TypeDescriptor {