From e7b5b55f6718b25a437a891a06a26c21384b6818 Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Wed, 18 Jan 2023 20:15:12 +0100 Subject: [PATCH] Add format column to post tables --- src/post/draft_post.go | 12 +++++----- src/post/draft_post_test.go | 1 + src/post/format.go | 44 +++++++++++++++++++++++++++++++++++++ src/post/post.go | 15 +++++++------ src/post/post_test.go | 10 +++++---- src/post/sql.go | 7 ++++++ 6 files changed, 73 insertions(+), 16 deletions(-) create mode 100644 src/post/format.go diff --git a/src/post/draft_post.go b/src/post/draft_post.go index 0cb2ee1..1c0e075 100644 --- a/src/post/draft_post.go +++ b/src/post/draft_post.go @@ -49,22 +49,24 @@ func (s *draftStore) Set(post Post) error { _, err = s.db.db.Exec( `INSERT INTO post_drafts ( - id, title, description, tags, series, body + id, title, description, tags, series, body, format ) VALUES - (?, ?, ?, ?, ?, ?) + (?, ?, ?, ?, ?, ?, ?) ON CONFLICT (id) DO UPDATE SET title=excluded.title, description=excluded.description, tags=excluded.tags, series=excluded.series, - body=excluded.body`, + body=excluded.body, + format=excluded.format`, post.ID, post.Title, &sql.NullString{String: post.Description, Valid: len(post.Description) > 0}, &sql.NullString{String: string(tagsJSON), Valid: len(post.Tags) > 0}, &sql.NullString{String: post.Series, Valid: post.Series != ""}, post.Body, + post.Format, ) if err != nil { @@ -86,7 +88,7 @@ func (s *draftStore) get( query := ` SELECT - p.id, p.title, p.description, p.tags, p.series, p.body + p.id, p.title, p.description, p.tags, p.series, p.body, p.format FROM post_drafts p ` + where + ` ORDER BY p.id ASC` @@ -118,7 +120,7 @@ func (s *draftStore) get( err := rows.Scan( &post.ID, &post.Title, &description, &tags, &series, - &post.Body, + &post.Body, &post.Format, ) if err != nil { diff --git a/src/post/draft_post_test.go b/src/post/draft_post_test.go index fde5f95..f7b7de2 100644 --- a/src/post/draft_post_test.go +++ b/src/post/draft_post_test.go @@ -72,6 +72,7 @@ func TestDraftStore(t *testing.T) { post.Series = "whatever" post.Body = "anything" post.Tags = []string{"bar", "baz"} + post.Format = FormatGemtext err = h.store.Set(post) assert.NoError(t, err) diff --git a/src/post/format.go b/src/post/format.go new file mode 100644 index 0000000..21ef79f --- /dev/null +++ b/src/post/format.go @@ -0,0 +1,44 @@ +package post + +import "errors" + +// ErrFormatStringMalformed indicates that a string could not be converted to a +// Format. +var ErrFormatStringMalformed = errors.New("format string malformed") + +// Format describes the format of the body of a Post. +type Format string + +// Enumeration of possible formats. +const ( + FormatMarkdown Format = "md" + FormatGemtext Format = "gmi" +) + +// Formats slice of all possible Formats. +var Formats = []Format{ + FormatMarkdown, + FormatGemtext, +} + +var strsToFormats = func() map[string]Format { + + m := map[string]Format{} + + for _, f := range Formats { + m[string(f)] = f + } + + return m +}() + +// FormatFromString parses a string into a Format, or returns +// ErrFormatStringMalformed. +func FormatFromString(str string) (Format, error) { + + if f, ok := strsToFormats[str]; ok { + return f, nil + } + + return "", ErrFormatStringMalformed +} diff --git a/src/post/post.go b/src/post/post.go index 1bdf03e..03326ad 100644 --- a/src/post/post.go +++ b/src/post/post.go @@ -34,6 +34,7 @@ type Post struct { Tags []string // only alphanumeric supported Series string Body string + Format Format } // StoredPost is a Post which has been stored in a Store, and has been given @@ -103,22 +104,24 @@ func (s *store) Set(post Post, now time.Time) (bool, error) { _, err := tx.Exec( `INSERT INTO posts ( - id, title, description, series, published_at, body + id, title, description, series, published_at, body, format ) VALUES - (?, ?, ?, ?, ?, ?) + (?, ?, ?, ?, ?, ?, ?) ON CONFLICT (id) DO UPDATE SET title=excluded.title, description=excluded.description, series=excluded.series, last_updated_at=?, - body=excluded.body`, + body=excluded.body, + format=excluded.format`, post.ID, post.Title, &sql.NullString{String: post.Description, Valid: len(post.Description) > 0}, &sql.NullString{String: post.Series, Valid: post.Series != ""}, nowSQL, post.Body, + post.Format, nowSQL, ) @@ -174,8 +177,7 @@ func (s *store) get( query := ` SELECT p.id, p.title, p.description, p.series, GROUP_CONCAT(pt.tag), - p.published_at, p.last_updated_at, - p.body + p.published_at, p.last_updated_at, p.body, p.format FROM posts p LEFT JOIN post_tags pt ON (p.id = pt.post_id) ` + where + ` @@ -208,8 +210,7 @@ func (s *store) get( err := rows.Scan( &post.ID, &post.Title, &description, &series, &tag, - &publishedAt, &lastUpdatedAt, - &post.Body, + &publishedAt, &lastUpdatedAt, &post.Body, &post.Format, ) if err != nil { diff --git a/src/post/post_test.go b/src/post/post_test.go index 7e87200..4c7f517 100644 --- a/src/post/post_test.go +++ b/src/post/post_test.go @@ -37,9 +37,10 @@ func TestNewID(t *testing.T) { func testPost(i int) Post { istr := strconv.Itoa(i) return Post{ - ID: istr, - Title: istr, - Body: istr, + ID: istr, + Title: istr, + Body: istr, + Format: FormatMarkdown, } } @@ -108,7 +109,7 @@ func TestStore(t *testing.T) { post.Tags = []string{"foo", "bar"} first, err := h.store.Set(post, now) - assert.NoError(t, err) + assert.NoError(t, err, "post:%+v", post) assert.True(t, first) gotPost, err := h.store.GetByID(post.ID) @@ -130,6 +131,7 @@ func TestStore(t *testing.T) { post.Series = "whatever" post.Body = "anything" post.Tags = []string{"bar", "baz"} + post.Format = FormatGemtext first, err = h.store.Set(post, newNow) assert.NoError(t, err) diff --git a/src/post/sql.go b/src/post/sql.go index 786ea63..c7b726f 100644 --- a/src/post/sql.go +++ b/src/post/sql.go @@ -66,6 +66,13 @@ var migrations = &migrate.MemoryMigrationSource{Migrations: []*migrate.Migration `ALTER TABLE posts DROP COLUMN description_old`, }, }, + { + Id: "4", + Up: []string{ + `ALTER TABLE post_drafts ADD COLUMN format TEXT DEFAULT 'md'`, + `ALTER TABLE posts ADD COLUMN format TEXT DEFAULT 'md'`, + }, + }, }} // SQLDB is a sqlite3 database which can be used by storage interfaces within