From a8c7defbc6da06b8f3c78ebc897f532bc0f95f09 Mon Sep 17 00:00:00 2001 From: Joshua Hawxwell Date: Mon, 8 Jan 2024 15:12:03 +0000 Subject: [PATCH] Marshal the shared.Date time value Currently only IsMalformed is being stored in Dynamo, as it only uses named fields. Since a shared.Date where IsMalformed is true will give an error to whatever is creating it I have chosen to Marshal as a date string. --- go.mod | 4 +++ go.sum | 10 ++++++++ internal/shared/date.go | 42 +++++++++++++++++++++++++------ internal/shared/date_test.go | 48 ++++++++++++++++++++++++++---------- 4 files changed, 84 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index 81d1ea0f..a8d620b8 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,10 @@ require ( require ( github.com/andybalholm/brotli v1.0.6 // indirect + github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.12.14 // indirect + github.com/aws/aws-sdk-go-v2/service/dynamodb v1.26.8 // indirect + github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.18.7 // indirect + github.com/aws/smithy-go v1.19.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect diff --git a/go.sum b/go.sum index 5d7e8882..2f85cebc 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,18 @@ github.com/aws/aws-sdk-go v1.49.13 h1:f4mGztsgnx2dR9r8FQYa9YW/RsKb+N7bgef4UGrOW1 github.com/aws/aws-sdk-go v1.49.13/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/aws/aws-sdk-go v1.49.15 h1:aH9bSV4kL4ziH0AMtuYbukGIVebXddXBL0cKZ1zj15k= github.com/aws/aws-sdk-go v1.49.15/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU= +github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4= +github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.12.14 h1:FpgWcv1aqU3xXbMVwEBr2sCeRT1Cctwqg/sWMI4wLoo= +github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.12.14/go.mod h1:J2zgl/oFM9OWQoaEATWvh426859hrB1cuVEqLgGpi+Q= +github.com/aws/aws-sdk-go-v2/service/dynamodb v1.26.8 h1:XKO0BswTDeZMLDBd/b5pCEZGttNXrzRUVtFvp2Ak/Vo= +github.com/aws/aws-sdk-go-v2/service/dynamodb v1.26.8/go.mod h1:N5tqZcYMM0N1PN7UQYJNWuGyO886OfnMhf/3MAbqMcI= +github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.18.7 h1:srShyROqxzC7p18Ws8mqM2sqxJO/8L3Kpiqf+NboJLg= +github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.18.7/go.mod h1:9efZgg4nJCGRp91MuHhkwd2kvyp7PWLRYYk5WjEQ5ts= github.com/aws/aws-xray-sdk-go v1.8.3 h1:S8GdgVncBRhzbNnNUgTPwhEqhwt2alES/9rLASyhxjU= github.com/aws/aws-xray-sdk-go v1.8.3/go.mod h1:tv8uLMOSCABolrIF8YCcp3ghyswArsan8dfLCA1ZATk= +github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM= +github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= diff --git a/internal/shared/date.go b/internal/shared/date.go index 5a85bf8c..df8eb2fd 100644 --- a/internal/shared/date.go +++ b/internal/shared/date.go @@ -2,6 +2,9 @@ package shared import ( "time" + + "github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue" + "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" ) type Date struct { @@ -9,15 +12,40 @@ type Date struct { IsMalformed bool } -func (m *Date) UnmarshalJSON(data []byte) error { - str := string(data) - time, err := time.Parse(`"2006-01-02"`, str) - - m.Time = time +func (d *Date) UnmarshalJSON(data []byte) error { + end := len(data) - 1 + if len(data) <= 2 || data[0] != '"' || data[end] != '"' { + d.IsMalformed = len(data) != 0 && len(data) != 2 && data[0] != '"' && data[end] != '"' + return nil + } - if err != nil { - m.IsMalformed = str != "" && str != `""` + if err := d.UnmarshalText(data[1:end]); err != nil { + d.IsMalformed = true } return nil } + +func (d *Date) UnmarshalText(data []byte) error { + var err error + d.Time, err = time.Parse(time.DateOnly, string(data)) + return err +} + +func (d *Date) UnmarshalDynamoDBAttributeValue(av types.AttributeValue) error { + var s string + if err := attributevalue.Unmarshal(av, &s); err != nil { + return err + } + + return d.UnmarshalText([]byte(s)) +} + +func (d Date) MarshalDynamoDBAttributeValue() (types.AttributeValue, error) { + text := "" + if !d.IsZero() { + text = d.Time.Format(time.DateOnly) + } + + return attributevalue.Marshal(text) +} diff --git a/internal/shared/date_test.go b/internal/shared/date_test.go index 7ec09600..5ac3b3fd 100644 --- a/internal/shared/date_test.go +++ b/internal/shared/date_test.go @@ -1,51 +1,73 @@ package shared import ( + "encoding/json" "testing" + "time" + "github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue" + "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" "github.com/stretchr/testify/assert" ) func TestUnmarshalDate(t *testing.T) { - testCases := []struct { + testCases := map[string]struct { name string in string expectFormatted string expectIsMalformed bool }{ - { - name: "ok", + "ok": { in: `"1930-10-31"`, expectFormatted: "31 October 1930", expectIsMalformed: false, }, - { - name: "out of bounds", + "out of bounds": { in: `"1930-11-31"`, expectFormatted: "1 January 0001", expectIsMalformed: true, }, - { - name: "invalid string", + "invalid string": { in: `"31 October 1930"`, expectFormatted: "1 January 0001", expectIsMalformed: true, }, - { - name: "number", + "number": { in: `1700240133`, expectFormatted: "1 January 0001", expectIsMalformed: true, }, + "empty string": { + in: `""`, + expectFormatted: "1 January 0001", + expectIsMalformed: false, + }, + "space": { + in: `" "`, + expectFormatted: "1 January 0001", + expectIsMalformed: true, + }, } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - date := Date{} - date.UnmarshalJSON([]byte(tc.in)) + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + var date Date + err := json.Unmarshal([]byte(tc.in), &date) + assert.Nil(t, err) assert.Equal(t, tc.expectFormatted, date.Time.Format("2 January 2006")) assert.Equal(t, tc.expectIsMalformed, date.IsMalformed) }) } } + +func TestDateDynamoDB(t *testing.T) { + in := &types.AttributeValueMemberS{Value: "2000-01-02"} + + var v Date + attributevalue.Unmarshal(in, &v) + assert.Equal(t, Date{Time: time.Date(2000, time.January, 2, 0, 0, 0, 0, time.UTC)}, v) + + out, _ := attributevalue.Marshal(v) + assert.Equal(t, in, out) +}