Use unix timestamps for post.Store

This commit is contained in:
Brian Picciano 2022-05-06 21:49:00 -06:00
parent f7d72adfb5
commit c99b37c5b3
2 changed files with 47 additions and 32 deletions

View File

@ -6,10 +6,10 @@ import (
"fmt" "fmt"
"path" "path"
"strings" "strings"
"time"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3" // we need dis
migrate "github.com/rubenv/sql-migrate" migrate "github.com/rubenv/sql-migrate"
"github.com/tilinna/clock"
) )
var ( var (
@ -23,8 +23,8 @@ var (
type StoredPost struct { type StoredPost struct {
Post Post
PublishedAt Date PublishedAt time.Time
LastUpdatedAt Date LastUpdatedAt time.Time
} }
// URL returns the relative URL of the StoredPost. // URL returns the relative URL of the StoredPost.
@ -32,9 +32,9 @@ func (p StoredPost) URL() string {
return path.Join( return path.Join(
fmt.Sprintf( fmt.Sprintf(
"%d/%0d/%0d", "%d/%0d/%0d",
p.PublishedAt.Year, p.PublishedAt.Year(),
p.PublishedAt.Month, p.PublishedAt.Month(),
p.PublishedAt.Day, p.PublishedAt.Day(),
), ),
p.ID+".html", p.ID+".html",
) )
@ -45,7 +45,7 @@ type Store interface {
// Set sets the Post data into the storage, keyed by the Post's ID. It // Set sets the Post data into the storage, keyed by the Post's ID. It
// overwrites a previous Post with the same ID, if there was one. // overwrites a previous Post with the same ID, if there was one.
Set(Post) error Set(post Post, now time.Time) error
// Get returns count StoredPosts, sorted time descending, offset by the given page // Get returns count StoredPosts, sorted time descending, offset by the given page
// number. The returned boolean indicates if there are more pages or not. // number. The returned boolean indicates if there are more pages or not.
@ -102,8 +102,6 @@ type StoreParams struct {
// Path to the file the database will be stored at. // Path to the file the database will be stored at.
DBFilePath string DBFilePath string
Clock clock.Clock
} }
type store struct { type store struct {
@ -136,7 +134,7 @@ func (s *store) Close() error {
return s.db.Close() return s.db.Close()
} }
// if the callback returns an error then the transaction is aborted // if the callback returns an error then the transaction is aborted.
func (s *store) withTx(cb func(*sql.Tx) error) error { func (s *store) withTx(cb func(*sql.Tx) error) error {
tx, err := s.db.Begin() tx, err := s.db.Begin()
@ -164,9 +162,12 @@ func (s *store) withTx(cb func(*sql.Tx) error) error {
return nil return nil
} }
func (s *store) Set(post Post) error { func (s *store) Set(post Post, now time.Time) error {
return s.withTx(func(tx *sql.Tx) error { return s.withTx(func(tx *sql.Tx) error {
currentDate := DateFromTime(s.params.Clock.Now())
nowTS := now.Unix()
nowSql := sql.NullInt64{Int64: nowTS, Valid: !now.IsZero()}
_, err := tx.Exec( _, err := tx.Exec(
`INSERT INTO posts ( `INSERT INTO posts (
@ -184,9 +185,9 @@ func (s *store) Set(post Post) error {
post.Title, post.Title,
post.Description, post.Description,
&sql.NullString{String: post.Series, Valid: post.Series != ""}, &sql.NullString{String: post.Series, Valid: post.Series != ""},
currentDate, nowSql,
post.Body, post.Body,
currentDate, nowSql,
) )
if err != nil { if err != nil {
@ -257,13 +258,14 @@ func (s *store) get(
for rows.Next() { for rows.Next() {
var ( var (
post StoredPost post StoredPost
series, tag sql.NullString series, tag sql.NullString
publishedAt, lastUpdatedAt sql.NullInt64
) )
err := rows.Scan( err := rows.Scan(
&post.ID, &post.Title, &post.Description, &series, &tag, &post.ID, &post.Title, &post.Description, &series, &tag,
&post.PublishedAt, &post.LastUpdatedAt, &publishedAt, &lastUpdatedAt,
&post.Body, &post.Body,
) )
@ -283,6 +285,14 @@ func (s *store) get(
post.Series = series.String post.Series = series.String
if publishedAt.Valid {
post.PublishedAt = time.Unix(publishedAt.Int64, 0).UTC()
}
if lastUpdatedAt.Valid {
post.LastUpdatedAt = time.Unix(lastUpdatedAt.Int64, 0).UTC()
}
posts = append(posts, post) posts = append(posts, post)
} }

View File

@ -29,7 +29,7 @@ type storeTestHarness struct {
func newStoreTestHarness(t *testing.T) storeTestHarness { func newStoreTestHarness(t *testing.T) storeTestHarness {
clock := clock.NewMock(time.Now().Truncate(1 * time.Hour)) clock := clock.NewMock(time.Now().UTC().Truncate(1 * time.Hour))
tmpFile, err := ioutil.TempFile(os.TempDir(), "mediocre-blog-post-store-test-") tmpFile, err := ioutil.TempFile(os.TempDir(), "mediocre-blog-post-store-test-")
if err != nil { if err != nil {
@ -48,7 +48,6 @@ func newStoreTestHarness(t *testing.T) storeTestHarness {
store, err := NewStore(StoreParams{ store, err := NewStore(StoreParams{
DBFilePath: tmpFilePath, DBFilePath: tmpFilePath,
Clock: clock,
}) })
assert.NoError(t, err) assert.NoError(t, err)
@ -66,7 +65,7 @@ func (h *storeTestHarness) testStoredPost(i int) StoredPost {
post := testPost(i) post := testPost(i)
return StoredPost{ return StoredPost{
Post: post, Post: post,
PublishedAt: DateFromTime(h.clock.Now()), PublishedAt: h.clock.Now(),
} }
} }
@ -101,41 +100,41 @@ func TestStore(t *testing.T) {
t.Run("set_get_delete", func(t *testing.T) { t.Run("set_get_delete", func(t *testing.T) {
h := newStoreTestHarness(t) h := newStoreTestHarness(t)
nowDate := DateFromTime(h.clock.Now()) now := h.clock.Now().UTC()
post := testPost(0) post := testPost(0)
post.Tags = []string{"foo", "bar"} post.Tags = []string{"foo", "bar"}
assert.NoError(t, h.store.Set(post)) assert.NoError(t, h.store.Set(post, now))
gotPost, err := h.store.GetByID(post.ID) gotPost, err := h.store.GetByID(post.ID)
assert.NoError(t, err) assert.NoError(t, err)
assertPostEqual(t, StoredPost{ assertPostEqual(t, StoredPost{
Post: post, Post: post,
PublishedAt: nowDate, PublishedAt: now,
}, gotPost) }, gotPost)
// we will now try updating the post on a different day, and ensure it // we will now try updating the post on a different day, and ensure it
// updates properly // updates properly
h.clock.Add(24 * time.Hour) h.clock.Add(24 * time.Hour)
newNowDate := DateFromTime(h.clock.Now()) newNow := h.clock.Now().UTC()
post.Title = "something else" post.Title = "something else"
post.Series = "whatever" post.Series = "whatever"
post.Body = "anything" post.Body = "anything"
post.Tags = []string{"bar", "baz"} post.Tags = []string{"bar", "baz"}
assert.NoError(t, h.store.Set(post)) assert.NoError(t, h.store.Set(post, newNow))
gotPost, err = h.store.GetByID(post.ID) gotPost, err = h.store.GetByID(post.ID)
assert.NoError(t, err) assert.NoError(t, err)
assertPostEqual(t, StoredPost{ assertPostEqual(t, StoredPost{
Post: post, Post: post,
PublishedAt: nowDate, PublishedAt: now,
LastUpdatedAt: newNowDate, LastUpdatedAt: newNow,
}, gotPost) }, gotPost)
// delete the post, it should go away // delete the post, it should go away
@ -148,6 +147,8 @@ func TestStore(t *testing.T) {
t.Run("get", func(t *testing.T) { t.Run("get", func(t *testing.T) {
h := newStoreTestHarness(t) h := newStoreTestHarness(t)
now := h.clock.Now().UTC()
posts := []StoredPost{ posts := []StoredPost{
h.testStoredPost(0), h.testStoredPost(0),
h.testStoredPost(1), h.testStoredPost(1),
@ -156,7 +157,7 @@ func TestStore(t *testing.T) {
} }
for _, post := range posts { for _, post := range posts {
assert.NoError(t, h.store.Set(post.Post)) assert.NoError(t, h.store.Set(post.Post, now))
} }
gotPosts, hasMore, err := h.store.Get(0, 2) gotPosts, hasMore, err := h.store.Get(0, 2)
@ -170,7 +171,7 @@ func TestStore(t *testing.T) {
assertPostsEqual(t, posts[2:4], gotPosts) assertPostsEqual(t, posts[2:4], gotPosts)
posts = append(posts, h.testStoredPost(4)) posts = append(posts, h.testStoredPost(4))
assert.NoError(t, h.store.Set(posts[4].Post)) assert.NoError(t, h.store.Set(posts[4].Post, now))
gotPosts, hasMore, err = h.store.Get(1, 2) gotPosts, hasMore, err = h.store.Get(1, 2)
assert.NoError(t, err) assert.NoError(t, err)
@ -186,6 +187,8 @@ func TestStore(t *testing.T) {
t.Run("get_by_series", func(t *testing.T) { t.Run("get_by_series", func(t *testing.T) {
h := newStoreTestHarness(t) h := newStoreTestHarness(t)
now := h.clock.Now().UTC()
posts := []StoredPost{ posts := []StoredPost{
h.testStoredPost(0), h.testStoredPost(0),
h.testStoredPost(1), h.testStoredPost(1),
@ -198,7 +201,7 @@ func TestStore(t *testing.T) {
posts[2].Series = "bar" posts[2].Series = "bar"
for _, post := range posts { for _, post := range posts {
assert.NoError(t, h.store.Set(post.Post)) assert.NoError(t, h.store.Set(post.Post, now))
} }
fooPosts, err := h.store.GetBySeries("foo") fooPosts, err := h.store.GetBySeries("foo")
@ -218,6 +221,8 @@ func TestStore(t *testing.T) {
h := newStoreTestHarness(t) h := newStoreTestHarness(t)
now := h.clock.Now().UTC()
posts := []StoredPost{ posts := []StoredPost{
h.testStoredPost(0), h.testStoredPost(0),
h.testStoredPost(1), h.testStoredPost(1),
@ -230,7 +235,7 @@ func TestStore(t *testing.T) {
posts[2].Tags = []string{"bar"} posts[2].Tags = []string{"bar"}
for _, post := range posts { for _, post := range posts {
assert.NoError(t, h.store.Set(post.Post)) assert.NoError(t, h.store.Set(post.Post, now))
} }
fooPosts, err := h.store.GetByTag("foo") fooPosts, err := h.store.GetByTag("foo")