Skip to content

Commit

Permalink
service/dynamodb/dynamodbattribute: Add support for time alias. (#1520)
Browse files Browse the repository at this point in the history
The `dynamodbattribte` package handles aliases of builtin types (e.g. aliased strings, ints, etc.), but not `time.Time`. This PR adds support for aliased `time.Time` types. I added encode/decode tests that mirror the tests for non-aliased `time.Time`.

Modification of #1505
  • Loading branch information
jasdel committed Sep 12, 2017
1 parent 971ef06 commit 9b5aaeb
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 6 deletions.
11 changes: 6 additions & 5 deletions service/dynamodb/dynamodbattribute/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ var stringInterfaceMapType = reflect.TypeOf(map[string]interface{}(nil))
var byteSliceType = reflect.TypeOf([]byte(nil))
var byteSliceSlicetype = reflect.TypeOf([][]byte(nil))
var numberType = reflect.TypeOf(Number(""))
var timeType = reflect.TypeOf(time.Time{})

func (d *Decoder) decode(av *dynamodb.AttributeValue, v reflect.Value, fieldTag tag) error {
var u Unmarshaler
Expand Down Expand Up @@ -338,12 +339,12 @@ func (d *Decoder) decodeNumber(n *string, v reflect.Value, fieldTag tag) error {
}
v.SetFloat(i)
default:
if _, ok := v.Interface().(time.Time); ok && fieldTag.AsUnixTime {
if v.Type().ConvertibleTo(timeType) && fieldTag.AsUnixTime {
t, err := decodeUnixTime(*n)
if err != nil {
return err
}
v.Set(reflect.ValueOf(t))
v.Set(reflect.ValueOf(t).Convert(v.Type()))
return nil
}
return &UnmarshalTypeError{Value: "number", Type: v.Type()}
Expand Down Expand Up @@ -502,12 +503,12 @@ func (d *Decoder) decodeString(s *string, v reflect.Value, fieldTag tag) error {

// To maintain backwards compatibility with ConvertFrom family of methods which
// converted strings to time.Time structs
if _, ok := v.Interface().(time.Time); ok {
if v.Type().ConvertibleTo(timeType) {
t, err := time.Parse(time.RFC3339, *s)
if err != nil {
return err
}
v.Set(reflect.ValueOf(t))
v.Set(reflect.ValueOf(t).Convert(v.Type()))
return nil
}

Expand Down Expand Up @@ -564,7 +565,7 @@ func decodeUnixTime(n string) (time.Time, error) {
v, err := strconv.ParseInt(n, 10, 64)
if err != nil {
return time.Time{}, &UnmarshalError{
Err: err, Value: n, Type: reflect.TypeOf(time.Time{}),
Err: err, Value: n, Type: timeType,
}
}

Expand Down
28 changes: 28 additions & 0 deletions service/dynamodb/dynamodbattribute/decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -527,3 +527,31 @@ func TestDecodeUnixTime(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, expect, actual)
}

func TestDecodeAliasedUnixTime(t *testing.T) {
type A struct {
Normal AliasedTime
Tagged AliasedTime `dynamodbav:",unixtime"`
}

expect := A{
Normal: AliasedTime(time.Unix(123, 0).UTC()),
Tagged: AliasedTime(time.Unix(456, 0)),
}

input := &dynamodb.AttributeValue{
M: map[string]*dynamodb.AttributeValue{
"Normal": {
S: aws.String("1970-01-01T00:02:03Z"),
},
"Tagged": {
N: aws.String("456"),
},
},
}
actual := A{}

err := Unmarshal(input, &actual)
assert.NoError(t, err)
assert.Equal(t, expect, actual)
}
4 changes: 3 additions & 1 deletion service/dynamodb/dynamodbattribute/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,9 @@ func (e *Encoder) encode(av *dynamodb.AttributeValue, v reflect.Value, fieldTag
func (e *Encoder) encodeStruct(av *dynamodb.AttributeValue, v reflect.Value, fieldTag tag) error {
// To maintain backwards compatibility with ConvertTo family of methods which
// converted time.Time structs to strings
if t, ok := v.Interface().(time.Time); ok {
if v.Type().ConvertibleTo(timeType) {
var t time.Time
t = v.Convert(timeType).Interface().(time.Time)
if fieldTag.AsUnixTime {
return UnixTime(t).MarshalDynamoDBAttributeValue(av)
}
Expand Down
28 changes: 28 additions & 0 deletions service/dynamodb/dynamodbattribute/encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,31 @@ func TestEncodeUnixTime(t *testing.T) {
}
assert.Equal(t, expect, actual)
}

type AliasedTime time.Time

func TestEncodeAliasedUnixTime(t *testing.T) {
type A struct {
Normal AliasedTime
Tagged AliasedTime `dynamodbav:",unixtime"`
}

a := A{
Normal: AliasedTime(time.Unix(123, 0).UTC()),
Tagged: AliasedTime(time.Unix(456, 0)),
}

actual, err := Marshal(a)
assert.NoError(t, err)
expect := &dynamodb.AttributeValue{
M: map[string]*dynamodb.AttributeValue{
"Normal": {
S: aws.String("1970-01-01T00:02:03Z"),
},
"Tagged": {
N: aws.String("456"),
},
},
}
assert.Equal(t, expect, actual)
}

0 comments on commit 9b5aaeb

Please sign in to comment.