From ee0075ad77001a4de04dafa0eaf6f3bad299c2e1 Mon Sep 17 00:00:00 2001 From: Jingwen Peng Date: Thu, 5 Jan 2017 22:04:11 +0800 Subject: [PATCH] Build and unpack pointer values Signed-off-by: Jingwen Peng --- request/builder.go | 110 +++++++++++++++++---------------------- request/builder_test.go | 59 +++++++++++++-------- request/unpacker.go | 10 ++-- request/unpacker_test.go | 67 ++++++++++++++++-------- 4 files changed, 134 insertions(+), 112 deletions(-) diff --git a/request/builder.go b/request/builder.go index 8fe7591..8d4dcb3 100644 --- a/request/builder.go +++ b/request/builder.go @@ -92,11 +92,14 @@ func (b *Builder) parseRequestProperties() error { fields := reflect.ValueOf(b.operation.Properties).Elem() for i := 0; i < fields.NumField(); i++ { switch value := fields.Field(i).Interface().(type) { - case string: - propertiesMap[fields.Type().Field(i).Tag.Get("name")] = value - case int: - numberString := strconv.Itoa(int(value)) - propertiesMap[fields.Type().Field(i).Tag.Get("name")] = numberString + case *string: + if value != nil { + propertiesMap[fields.Type().Field(i).Tag.Get("name")] = *value + } + case *int: + if value != nil { + propertiesMap[fields.Type().Field(i).Tag.Get("name")] = strconv.Itoa(int(*value)) + } } } @@ -128,57 +131,47 @@ func (b *Builder) parseRequestParams() error { tagDefault := b.input.Elem().Type().Field(i).Tag.Get("default") if tagName != "" && tagLocation != "" && requestParams != nil { switch value := b.input.Elem().Field(i).Interface().(type) { - case string: - if value != "" { - requestParams[tagName] = value + case *string: + if tagDefault != "" { + requestParams[tagName] = tagDefault } - case int: - numberString := strconv.Itoa(int(value)) - if numberString == "0" { - numberString = "" - if tagDefault != "" { - numberString = tagDefault - } + if value != nil { + requestParams[tagName] = *value } - if numberString != "" { - requestParams[tagName] = numberString + case *int: + if tagDefault != "" { + requestParams[tagName] = tagDefault } - case bool: - case time.Time: - zero := time.Time{} - if value != zero { - var timeString string + if value != nil { + requestParams[tagName] = strconv.Itoa(int(*value)) + } + case *bool: + case *time.Time: + if tagDefault != "" { + requestParams[tagName] = tagDefault + } + if value != nil { format := b.input.Elem().Type().Field(i).Tag.Get("format") - timeString = utils.TimeToString(value, format) - if timeString != "" { - requestParams[tagName] = timeString + requestParams[tagName] = utils.TimeToString(*value, format) + } + case []*string: + for index, item := range value { + key := tagName + "." + strconv.Itoa(index+1) + if tagDefault != "" { + requestParams[tagName] = tagDefault + } + if item != nil { + requestParams[key] = *item } } - case []string: - if len(value) > 0 { - for index, item := range value { - key := tagName + "." + strconv.Itoa(index+1) - requestParams[key] = item + case []*int: + for index, item := range value { + key := tagName + "." + strconv.Itoa(index+1) + if tagDefault != "" { + requestParams[tagName] = tagDefault } - } - case []int: - if len(value) > 0 { - numbersString := []string{} - for _, number := range value { - numberString := strconv.Itoa(int(number)) - if numberString == "0" { - numberString = "" - if tagDefault != "" { - numberString = tagDefault - } - } - if numberString != "" { - numbersString = append(numbersString, numberString) - } - } - - for index, item := range numbersString { - requestParams[tagName+"."+strconv.Itoa(index+1)] = item + if item != nil { + requestParams[key] = strconv.Itoa(int(*item)) } } default: @@ -197,20 +190,13 @@ func (b *Builder) parseRequestParams() error { tagKey := tagName + "." + strconv.Itoa(i+1) + "." + fieldTagName switch fieldValue := item.Field(j).Interface().(type) { - case int: - numberString := strconv.Itoa(int(fieldValue)) - if numberString == "0" { - numberString = "" - if tagDefault != "" { - numberString = tagDefault - } + case *int: + if fieldValue != nil { + requestParams[tagKey] = strconv.Itoa(int(*fieldValue)) } - if numberString != "" { - requestParams[tagKey] = numberString - } - case string: - if fieldValue != "" { - requestParams[tagKey] = fieldValue + case *string: + if fieldValue != nil { + requestParams[tagKey] = *fieldValue } } } diff --git a/request/builder_test.go b/request/builder_test.go index 373de46..95a05a9 100644 --- a/request/builder_test.go +++ b/request/builder_test.go @@ -27,25 +27,42 @@ import ( ) type InstanceServiceProperties struct { - Zone string `json:"zone" name:"zone"` // Required + Zone *string `json:"zone" name:"zone"` // Required } + type DescribeInstancesInput struct { - ImageID []string `json:"image_id" name:"image_id" location:"params"` - InstanceClass int `json:"instance_class" name:"instance_class" location:"params" default:"0"` // Available values: 0, 1 - InstanceType []string `json:"instance_type" name:"instance_type" location:"params"` - Instances []string `json:"instances" name:"instances" location:"params"` - Limit int `json:"limit" name:"limit" omitEmpty:"true" location:"params"` - Offset int `json:"offset" name:"offset" location:"params"` - SearchWord string `json:"search_word" name:"search_word" location:"params"` - Status []string `json:"status" name:"status" location:"params"` // Available values: pending, running, stopped, suspended, terminated, ceased - Tags []string `json:"tags" name:"tags" location:"params"` - Verbose int `json:"verbose" name:"verbose" location:"params"` // Available values: 0, 1 + ImageID []*string `json:"image_id" name:"image_id" location:"params"` + InstanceClass *int `json:"instance_class" name:"instance_class" location:"params" default:"0"` // Available values: 0, 1 + InstanceType []*string `json:"instance_type" name:"instance_type" location:"params"` + Instances []*string `json:"instances" name:"instances" location:"params"` + Limit *int `json:"limit" name:"limit" location:"params"` + Offset *int `json:"offset" name:"offset" location:"params"` + SearchWord *string `json:"search_word" name:"search_word" location:"params"` + Status []*string `json:"status" name:"status" location:"params"` // Available values: pending, running, stopped, suspended, terminated, ceased + Tags []*string `json:"tags" name:"tags" location:"params"` + Verbose *int `json:"verbose" name:"verbose" location:"params"` // Available values: 0, 1 } func (i *DescribeInstancesInput) Validate() error { return nil } +func String(v string) *string { + return &v +} + +func StringSlice(src []string) []*string { + dst := make([]*string, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +func Int(v int) *int { + return &v +} + func TestBuilder(t *testing.T) { conf, err := config.NewDefault() @@ -56,7 +73,7 @@ func TestBuilder(t *testing.T) { operation := &data.Operation{ Config: conf, Properties: &InstanceServiceProperties{ - Zone: "beta", + Zone: String("beta"), }, APIName: "DescribeInstances", ServiceName: "Instance", @@ -67,16 +84,14 @@ func TestBuilder(t *testing.T) { }, } inputValue := reflect.ValueOf(&DescribeInstancesInput{ - ImageID: []string{"img-xxxxxxxx", "img-zzzzzzzz"}, - InstanceClass: 0, - InstanceType: []string{"type1", "type2"}, - Instances: []string{"i-xxxxxxxx", "i-zzzzzzzz"}, - Limit: 0, - Offset: 0, - SearchWord: "search_word", - Status: []string{"running"}, - Tags: []string{"tag1", "tag2"}, - Verbose: 1, + ImageID: StringSlice([]string{"img-xxxxxxxx", "img-zzzzzzzz"}), + InstanceClass: Int(0), + InstanceType: StringSlice([]string{"type1", "type2"}), + Instances: StringSlice([]string{"i-xxxxxxxx", "i-zzzzzzzz"}), + SearchWord: String("search_word"), + Status: StringSlice([]string{"running"}), + Tags: StringSlice([]string{"tag1", "tag2"}), + Verbose: Int(1), }) httpRequest, err := builder.BuildHTTPRequest(operation, &inputValue) assert.Nil(t, err) diff --git a/request/unpacker.go b/request/unpacker.go index 1963bf1..c6fec4a 100644 --- a/request/unpacker.go +++ b/request/unpacker.go @@ -122,13 +122,13 @@ func (u *Unpacker) parseError() error { retCodeValue := u.output.Elem().FieldByName("RetCode") messageValue := u.output.Elem().FieldByName("Message") - if retCodeValue.IsValid() && retCodeValue.Type().String() == "int" && - messageValue.IsValid() && messageValue.Type().String() == "string" && - retCodeValue.Int() != 0 { + if retCodeValue.IsValid() && retCodeValue.Type().String() == "*int" && + messageValue.IsValid() && messageValue.Type().String() == "*string" && + retCodeValue.Elem().Int() != 0 { return &errors.QingCloudError{ - RetCode: int(retCodeValue.Int()), - Message: messageValue.String(), + RetCode: int(retCodeValue.Elem().Int()), + Message: messageValue.Elem().String(), } } diff --git a/request/unpacker_test.go b/request/unpacker_test.go index a197c67..7f17553 100644 --- a/request/unpacker_test.go +++ b/request/unpacker_test.go @@ -30,33 +30,54 @@ import ( "github.com/yunify/qingcloud-sdk-go/request/errors" ) +func StringValue(v *string) string { + if v != nil { + return *v + } + return "" +} + +func IntValue(v *int) int { + if v != nil { + return *v + } + return 0 +} + +func TimeValue(v *time.Time) time.Time { + if v != nil { + return *v + } + return time.Time{} +} + func TestUnpackerUnpackHTTPRequest(t *testing.T) { type Instance struct { - Device string `json:"device" name:"device"` - InstanceID string `json:"instance_id" name:"instance_id"` - InstanceName string `json:"instance_name" name:"instance_name"` + Device *string `json:"device" name:"device"` + InstanceID *string `json:"instance_id" name:"instance_id"` + InstanceName *string `json:"instance_name" name:"instance_name"` } type Volume struct { - CreateTime time.Time `json:"create_time" name:"create_time" format:"ISO 8601"` - Description string `json:"description" name:"description"` - Instance *Instance `json:"instance" name:"instance"` - Size int `json:"size" name:"size"` - Status string `json:"status" name:"status"` - StatusTime time.Time `json:"status_time" name:"status_time" format:"ISO 8601"` - SubCode int `json:"sub_code" name:"sub_code"` - TransitionStatus string `json:"transition_status" name:"transition_status"` - VolumeID string `json:"volume_id" name:"volume_id"` - VolumeName string `json:"volume_name" name:"volume_name"` + CreateTime *time.Time `json:"create_time" name:"create_time" format:"ISO 8601"` + Description *string `json:"description" name:"description"` + Instance *Instance `json:"instance" name:"instance"` + Size *int `json:"size" name:"size"` + Status *string `json:"status" name:"status"` + StatusTime *time.Time `json:"status_time" name:"status_time" format:"ISO 8601"` + SubCode *int `json:"sub_code" name:"sub_code"` + TransitionStatus *string `json:"transition_status" name:"transition_status"` + VolumeID *string `json:"volume_id" name:"volume_id"` + VolumeName *string `json:"volume_name" name:"volume_name"` } type DescribeVolumesOutput struct { StatusCode int `location:"statusCode"` Error *errors.QingCloudError - Action string `json:"action" name:"action"` - RetCode int `json:"ret_code" name:"ret_code"` - TotalCount int `json:"total_count" name:"total_count"` + Action *string `json:"action" name:"action"` + RetCode *int `json:"ret_code" name:"ret_code"` + TotalCount *int `json:"total_count" name:"total_count"` VolumeSet []*Volume `json:"volume_set" name:"volume_set"` } @@ -94,18 +115,18 @@ func TestUnpackerUnpackHTTPRequest(t *testing.T) { unpacker := Unpacker{} err := unpacker.UnpackHTTPRequest(&data.Operation{}, httpResponse, &outputValue) assert.Nil(t, err) - assert.Equal(t, "i-xxxxxxxx", output.VolumeSet[0].Instance.InstanceID) - assert.Equal(t, "vol-xxxxxxxx", output.VolumeSet[0].VolumeID) - assert.Equal(t, "vol name", output.VolumeSet[0].VolumeName) - assert.Equal(t, 1024, output.TotalCount) + assert.Equal(t, "i-xxxxxxxx", StringValue(output.VolumeSet[0].Instance.InstanceID)) + assert.Equal(t, "vol-xxxxxxxx", StringValue(output.VolumeSet[0].VolumeID)) + assert.Equal(t, "vol name", StringValue(output.VolumeSet[0].VolumeName)) + assert.Equal(t, 1024, IntValue(output.TotalCount)) statusTime := time.Date(2013, 8, 30, 5, 13, 32, 0, time.UTC) - assert.Equal(t, statusTime, output.VolumeSet[0].StatusTime) + assert.Equal(t, statusTime, TimeValue(output.VolumeSet[0].StatusTime)) } func TestUnpacker_UnpackHTTPRequestWithError(t *testing.T) { type DescribeInstanceTypesOutput struct { - RetCode int `json:"ret_code" name:"ret_code"` - Message string `json:"message" name:"message"` + RetCode *int `json:"ret_code" name:"ret_code"` + Message *string `json:"message" name:"message"` } httpResponse := &http.Response{Header: http.Header{}}