From a54efa09f9fd1b8af9d99e0e94600133a20b751c Mon Sep 17 00:00:00 2001 From: tobi <31960611+tsmethurst@users.noreply.github.com> Date: Tue, 24 May 2022 18:21:27 +0200 Subject: [PATCH] [chore] Serialize times as UTC ISO8601 instead of RFC3339 (#602) * add time util to mimic utc ISO8601 * use ISO8601 when serializing to frontend * update test notification --- .../processing/streaming/notification_test.go | 4 +- internal/typeutils/internaltofrontend.go | 16 +++--- internal/util/time.go | 30 +++++++++++ internal/util/time_test.go | 53 +++++++++++++++++++ 4 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 internal/util/time.go create mode 100644 internal/util/time_test.go diff --git a/internal/processing/streaming/notification_test.go b/internal/processing/streaming/notification_test.go index faaf01621..f26058744 100644 --- a/internal/processing/streaming/notification_test.go +++ b/internal/processing/streaming/notification_test.go @@ -44,7 +44,7 @@ func (suite *NotificationTestSuite) TestStreamNotification() { notification := &apimodel.Notification{ ID: "01FH57SJCMDWQGEAJ0X08CE3WV", Type: "follow", - CreatedAt: "2021-10-04T10:52:36+02:00", + CreatedAt: "2021-10-04T08:52:36Z", Account: followAccountAPIModel, } @@ -52,7 +52,7 @@ func (suite *NotificationTestSuite) TestStreamNotification() { suite.NoError(err) msg := <-openStream.Messages - suite.Equal(`{"id":"01FH57SJCMDWQGEAJ0X08CE3WV","type":"follow","created_at":"2021-10-04T10:52:36+02:00","account":{"id":"01F8MH5ZK5VRH73AKHQM6Y9VNX","username":"foss_satan","acct":"foss_satan@fossbros-anonymous.io","display_name":"big gerald","locked":false,"bot":false,"created_at":"2021-09-26T12:52:36+02:00","note":"i post about like, i dunno, stuff, or whatever!!!!","url":"http://fossbros-anonymous.io/@foss_satan","avatar":"","avatar_static":"","header":"","header_static":"","followers_count":0,"following_count":0,"statuses_count":1,"last_status_at":"2021-09-20T10:40:37Z","emojis":[],"fields":[]}}`, msg.Payload) + suite.Equal(`{"id":"01FH57SJCMDWQGEAJ0X08CE3WV","type":"follow","created_at":"2021-10-04T08:52:36Z","account":{"id":"01F8MH5ZK5VRH73AKHQM6Y9VNX","username":"foss_satan","acct":"foss_satan@fossbros-anonymous.io","display_name":"big gerald","locked":false,"bot":false,"created_at":"2021-09-26T10:52:36Z","note":"i post about like, i dunno, stuff, or whatever!!!!","url":"http://fossbros-anonymous.io/@foss_satan","avatar":"","avatar_static":"","header":"","header_static":"","followers_count":0,"following_count":0,"statuses_count":1,"last_status_at":"2021-09-20T10:40:37Z","emojis":[],"fields":[]}}`, msg.Payload) } func TestNotificationTestSuite(t *testing.T) { diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go index 9158830ef..9601f3e3a 100644 --- a/internal/typeutils/internaltofrontend.go +++ b/internal/typeutils/internaltofrontend.go @@ -22,7 +22,6 @@ import ( "context" "fmt" "strings" - "time" "github.com/sirupsen/logrus" "github.com/spf13/viper" @@ -31,6 +30,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/superseriousbusiness/gotosocial/internal/util" ) func (c *converter) AccountToAPIAccountSensitive(ctx context.Context, a *gtsmodel.Account) (*model.Account, error) { @@ -93,7 +93,7 @@ func (c *converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A var lastStatusAt string lastPosted, err := c.db.GetAccountLastPosted(ctx, a.ID) if err == nil && !lastPosted.IsZero() { - lastStatusAt = lastPosted.Format(time.RFC3339) + lastStatusAt = util.FormatISO8601(lastPosted) } // set account avatar fields if available @@ -140,7 +140,7 @@ func (c *converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A Value: f.Value, } if !f.VerifiedAt.IsZero() { - mField.VerifiedAt = f.VerifiedAt.Format(time.RFC3339) + mField.VerifiedAt = util.FormatISO8601(f.VerifiedAt) } fields = append(fields, mField) } @@ -169,7 +169,7 @@ func (c *converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A DisplayName: a.DisplayName, Locked: a.Locked, Bot: a.Bot, - CreatedAt: a.CreatedAt.Format(time.RFC3339), + CreatedAt: util.FormatISO8601(a.CreatedAt), Note: a.Note, URL: a.URL, Avatar: aviURL, @@ -209,7 +209,7 @@ func (c *converter) AccountToAPIAccountBlocked(ctx context.Context, a *gtsmodel. Acct: acct, DisplayName: a.DisplayName, Bot: a.Bot, - CreatedAt: a.CreatedAt.Format(time.RFC3339), + CreatedAt: util.FormatISO8601(a.CreatedAt), URL: a.URL, Suspended: suspended, }, nil @@ -511,7 +511,7 @@ func (c *converter) StatusToAPIStatus(ctx context.Context, s *gtsmodel.Status, r apiStatus := &model.Status{ ID: s.ID, - CreatedAt: s.CreatedAt.Format(time.RFC3339), + CreatedAt: util.FormatISO8601(s.CreatedAt), InReplyToID: s.InReplyToID, InReplyToAccountID: s.InReplyToAccountID, Sensitive: s.Sensitive, @@ -695,7 +695,7 @@ func (c *converter) NotificationToAPINotification(ctx context.Context, n *gtsmod return &model.Notification{ ID: n.ID, Type: string(n.NotificationType), - CreatedAt: n.CreatedAt.Format(time.RFC3339), + CreatedAt: util.FormatISO8601(n.CreatedAt), Account: apiAccount, Status: apiStatus, }, nil @@ -714,7 +714,7 @@ func (c *converter) DomainBlockToAPIDomainBlock(ctx context.Context, b *gtsmodel domainBlock.PrivateComment = b.PrivateComment domainBlock.SubscriptionID = b.SubscriptionID domainBlock.CreatedBy = b.CreatedByAccountID - domainBlock.CreatedAt = b.CreatedAt.Format(time.RFC3339) + domainBlock.CreatedAt = util.FormatISO8601(b.CreatedAt) } return domainBlock, nil diff --git a/internal/util/time.go b/internal/util/time.go new file mode 100644 index 000000000..d4d4ebf79 --- /dev/null +++ b/internal/util/time.go @@ -0,0 +1,30 @@ +/* + GoToSocial + Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package util + +import "time" + +// ISO8601 is a formatter for serializing times that forces ISO8601 behavior. +const ISO8601 = "2006-01-02T15:04:05Z" + +// FormatISO8601 converts the given time to UTC and then formats it +// using the ISO8601 const, which the Mastodon API is able to understand. +func FormatISO8601(t time.Time) string { + return t.UTC().Format(ISO8601) +} diff --git a/internal/util/time_test.go b/internal/util/time_test.go new file mode 100644 index 000000000..1d49e82af --- /dev/null +++ b/internal/util/time_test.go @@ -0,0 +1,53 @@ +/* + GoToSocial + Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package util_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + "github.com/superseriousbusiness/gotosocial/internal/util" + "github.com/superseriousbusiness/gotosocial/testrig" +) + +type TimeSuite struct { + suite.Suite +} + +func (suite *TimeSuite) TestISO8601Format1() { + testTime := testrig.TimeMustParse("2022-05-17T13:10:59Z") + testTimeString := util.FormatISO8601(testTime) + suite.Equal("2022-05-17T13:10:59Z", testTimeString) +} + +func (suite *TimeSuite) TestISO8601Format2() { + testTime := testrig.TimeMustParse("2022-05-09T07:34:35+02:00") + testTimeString := util.FormatISO8601(testTime) + suite.Equal("2022-05-09T05:34:35Z", testTimeString) +} + +func (suite *TimeSuite) TestISO8601Format3() { + testTime := testrig.TimeMustParse("2021-10-04T10:52:36+02:00") + testTimeString := util.FormatISO8601(testTime) + suite.Equal("2021-10-04T08:52:36Z", testTimeString) +} + +func TestTimeSuite(t *testing.T) { + suite.Run(t, &TimeSuite{}) +}