diff --git a/srv/src/cmd/import-posts/main.go b/srv/src/cmd/import-posts/main.go index 6a1e952..e4eb9e7 100644 --- a/srv/src/cmd/import-posts/main.go +++ b/srv/src/cmd/import-posts/main.go @@ -133,13 +133,13 @@ func main() { logger.FatalString(ctx, "no paths given") } - postStore, err := post.NewStore(post.StoreParams{ - DataDir: dataDir, - }) + postDB, err := post.NewSQLDB(dataDir) if err != nil { - logger.Fatal(ctx, "initializing post store", err) + logger.Fatal(ctx, "initializing post sql db", err) } - defer postStore.Close() + defer postDB.Close() + + postStore := post.NewStore(postDB) for _, path := range *paths { diff --git a/srv/src/post/sql.go b/srv/src/post/sql.go new file mode 100644 index 0000000..fb9468f --- /dev/null +++ b/srv/src/post/sql.go @@ -0,0 +1,69 @@ +package post + +import ( + "database/sql" + "fmt" + "path" + + "github.com/mediocregopher/blog.mediocregopher.com/srv/cfg" + migrate "github.com/rubenv/sql-migrate" +) + +var migrations = []*migrate.Migration{ + &migrate.Migration{ + Id: "1", + Up: []string{ + `CREATE TABLE posts ( + id TEXT NOT NULL PRIMARY KEY, + title TEXT NOT NULL, + description TEXT NOT NULL, + series TEXT, + + published_at INTEGER NOT NULL, + last_updated_at INTEGER, + + body TEXT NOT NULL + )`, + `CREATE TABLE post_tags ( + post_id TEXT NOT NULL, + tag TEXT NOT NULL, + UNIQUE(post_id, tag) + )`, + }, + Down: []string{ + "DROP TABLE post_tags", + "DROP TABLE posts", + }, + }, +} + +// SQLDB is a sqlite3 database which can be used by storage interfaces within +// this package. +type SQLDB struct { + db *sql.DB +} + +// NewSQLDB initializes and returns a new sqlite3 database for post storage +// intefaces. The db will be created within the given data directory. +func NewSQLDB(dataDir cfg.DataDir) (*SQLDB, error) { + + path := path.Join(dataDir.Path, "post.sqlite3") + + db, err := sql.Open("sqlite3", path) + if err != nil { + return nil, fmt.Errorf("opening sqlite file at %q: %w", path, err) + } + + migrations := &migrate.MemoryMigrationSource{Migrations: migrations} + + if _, err := migrate.Exec(db, "sqlite3", migrations, migrate.Up); err != nil { + return nil, fmt.Errorf("running migrations: %w", err) + } + + return &SQLDB{db}, nil +} + +// Close cleans up loose resources being held by the db. +func (db *SQLDB) Close() error { + return db.db.Close() +} diff --git a/srv/src/post/store.go b/srv/src/post/store.go index 6bdccc2..5da17a9 100644 --- a/srv/src/post/store.go +++ b/srv/src/post/store.go @@ -9,8 +9,6 @@ import ( "time" _ "github.com/mattn/go-sqlite3" // we need dis - "github.com/mediocregopher/blog.mediocregopher.com/srv/cfg" - migrate "github.com/rubenv/sql-migrate" ) var ( @@ -69,66 +67,15 @@ type Store interface { Close() error } -var migrations = []*migrate.Migration{ - &migrate.Migration{ - Id: "1", - Up: []string{ - `CREATE TABLE posts ( - id TEXT NOT NULL PRIMARY KEY, - title TEXT NOT NULL, - description TEXT NOT NULL, - series TEXT, - - published_at INTEGER NOT NULL, - last_updated_at INTEGER, - - body TEXT NOT NULL - )`, - `CREATE TABLE post_tags ( - post_id TEXT NOT NULL, - tag TEXT NOT NULL, - UNIQUE(post_id, tag) - )`, - }, - Down: []string{ - "DROP TABLE post_tags", - "DROP TABLE posts", - }, - }, -} - -// Params are parameters used to initialize a new Store. All fields are required -// unless otherwise noted. -type StoreParams struct { - DataDir cfg.DataDir -} - type store struct { - params StoreParams - db *sql.DB + db *sql.DB } -// NewStore initializes a new Store using a sqlite3 database at the given file -// path. -func NewStore(params StoreParams) (Store, error) { - - path := path.Join(params.DataDir.Path, "post.sqlite3") - - db, err := sql.Open("sqlite3", path) - if err != nil { - return nil, fmt.Errorf("opening sqlite file at %q: %w", path, err) - } - - migrations := &migrate.MemoryMigrationSource{Migrations: migrations} - - if _, err := migrate.Exec(db, "sqlite3", migrations, migrate.Up); err != nil { - return nil, fmt.Errorf("running migrations: %w", err) - } - +// NewStore initializes a new Store using an existing SQLDB. +func NewStore(db *SQLDB) Store { return &store{ - params: params, - db: db, - }, nil + db: db.db, + } } func (s *store) Close() error { @@ -168,7 +115,7 @@ func (s *store) Set(post Post, now time.Time) error { nowTS := now.Unix() - nowSql := sql.NullInt64{Int64: nowTS, Valid: !now.IsZero()} + nowSQL := sql.NullInt64{Int64: nowTS, Valid: !now.IsZero()} _, err := tx.Exec( `INSERT INTO posts ( @@ -186,9 +133,9 @@ func (s *store) Set(post Post, now time.Time) error { post.Title, post.Description, &sql.NullString{String: post.Series, Valid: post.Series != ""}, - nowSql, + nowSQL, post.Body, - nowSql, + nowSQL, ) if err != nil { diff --git a/srv/src/post/store_test.go b/srv/src/post/store_test.go index 1b4fb30..3ccf5d7 100644 --- a/srv/src/post/store_test.go +++ b/srv/src/post/store_test.go @@ -38,14 +38,14 @@ func newStoreTestHarness(t *testing.T) storeTestHarness { clock := clock.NewMock(time.Now().UTC().Truncate(1 * time.Hour)) - store, err := NewStore(StoreParams{ - DataDir: dataDir, - }) - assert.NoError(t, err) + db, err := NewSQLDB(dataDir) + if err != nil { + t.Fatal(err) + } - t.Cleanup(func() { - assert.NoError(t, store.Close()) - }) + t.Cleanup(func() { db.Close() }) + + store := NewStore(db) return storeTestHarness{ clock: clock,