Convert ScheduleBlock, SponsorSlide, and UserSession models to use Bolt DB and clean up remaining SQLite artifacts.

This commit is contained in:
Patrick Fairbank
2021-05-12 18:20:01 -07:00
parent ec71975e93
commit 6cea008524
17 changed files with 161 additions and 237 deletions

View File

@@ -1,18 +1,13 @@
// Copyright 2014 Team 254. All Rights Reserved.
// Author: pat@patfairbank.com (Patrick Fairbank)
//
// Functions for manipulating the per-event SQLite datastore.
// Functions for manipulating the per-event Bolt datastore.
package model
import (
"bitbucket.org/liamstask/goose/lib/goose"
"database/sql"
"encoding/json"
"fmt"
"github.com/Team254/cheesy-arena-lite/game"
"github.com/jmoiron/modl"
_ "github.com/mattn/go-sqlite3"
"go.etcd.io/bbolt"
"io"
"os"
@@ -22,16 +17,11 @@ import (
)
const backupsDir = "db/backups"
const migrationsDir = "db/migrations"
var BaseDir = "." // Mutable for testing
type Database struct {
Path string
db *sql.DB
sponsorSlideMap *modl.DbMap
scheduleBlockMap *modl.DbMap
userSessionMap *modl.DbMap
bolt *bbolt.DB
allianceTeamTable *table
awardTable *table
@@ -40,34 +30,17 @@ type Database struct {
matchTable *table
matchResultTable *table
rankingTable *table
scheduleBlockTable *table
sponsorSlideTable *table
teamTable *table
userSessionTable *table
}
// Opens the SQLite database at the given path, creating it if it doesn't exist, and runs any pending
// migrations.
// Opens the Bolt database at the given path, creating it if it doesn't exist.
func OpenDatabase(filename string) (*Database, error) {
// Find and run the migrations using goose. This also auto-creates the DB.
database := Database{Path: filename}
migrationsPath := filepath.Join(BaseDir, migrationsDir)
dbDriver := goose.DBDriver{"sqlite3", database.Path, "github.com/mattn/go-sqlite3", &goose.Sqlite3Dialect{}}
dbConf := goose.DBConf{MigrationsDir: migrationsPath, Env: "prod", Driver: dbDriver}
target, err := goose.GetMostRecentDBVersion(migrationsPath)
if err != nil {
return nil, err
}
err = goose.RunMigrations(&dbConf, migrationsPath, target)
if err != nil {
return nil, err
}
db, err := sql.Open("sqlite3", database.Path)
if err != nil {
return nil, err
}
database.db = db
database.mapTables()
database.bolt, err = bbolt.Open(database.Path+".bolt", 0644, &bbolt.Options{NoSync: true, Timeout: time.Second})
var err error
database.bolt, err = bbolt.Open(database.Path, 0644, &bbolt.Options{NoSync: true, Timeout: time.Second})
if err != nil {
return nil, err
}
@@ -94,15 +67,23 @@ func OpenDatabase(filename string) (*Database, error) {
if database.rankingTable, err = database.newTable(game.Ranking{}); err != nil {
return nil, err
}
if database.scheduleBlockTable, err = database.newTable(ScheduleBlock{}); err != nil {
return nil, err
}
if database.sponsorSlideTable, err = database.newTable(SponsorSlide{}); err != nil {
return nil, err
}
if database.teamTable, err = database.newTable(Team{}); err != nil {
return nil, err
}
if database.userSessionTable, err = database.newTable(UserSession{}); err != nil {
return nil, err
}
return &database, nil
}
func (database *Database) Close() error {
database.db.Close()
return database.bolt.Close()
}
@@ -130,26 +111,3 @@ func (database *Database) Backup(eventName, reason string) error {
}
return nil
}
// Sets up table-object associations.
func (database *Database) mapTables() {
dialect := new(modl.SqliteDialect)
database.sponsorSlideMap = modl.NewDbMap(database.db, dialect)
database.sponsorSlideMap.AddTableWithName(SponsorSlide{}, "sponsor_slides").SetKeys(true, "Id")
database.scheduleBlockMap = modl.NewDbMap(database.db, dialect)
database.scheduleBlockMap.AddTableWithName(ScheduleBlock{}, "schedule_blocks").SetKeys(true, "Id")
database.userSessionMap = modl.NewDbMap(database.db, dialect)
database.userSessionMap.AddTableWithName(UserSession{}, "user_sessions").SetKeys(true, "Id")
}
func serializeHelper(target *string, source interface{}) error {
bytes, err := json.Marshal(source)
if err != nil {
return err
}
*target = string(bytes)
return nil
}

View File

@@ -6,11 +6,12 @@
package model
import (
"sort"
"time"
)
type ScheduleBlock struct {
Id int
Id int `db:"id"`
MatchType string
StartTime time.Time
NumMatches int
@@ -18,21 +19,42 @@ type ScheduleBlock struct {
}
func (database *Database) CreateScheduleBlock(block *ScheduleBlock) error {
return database.scheduleBlockMap.Insert(block)
return database.scheduleBlockTable.create(block)
}
func (database *Database) GetScheduleBlocksByMatchType(matchType string) ([]ScheduleBlock, error) {
var blocks []ScheduleBlock
err := database.scheduleBlockMap.Select(&blocks, "SELECT * FROM schedule_blocks WHERE matchtype = ? ORDER BY "+
"starttime ", matchType)
return blocks, err
var scheduleBlocks []ScheduleBlock
if err := database.scheduleBlockTable.getAll(&scheduleBlocks); err != nil {
return nil, err
}
var matchingScheduleBlocks []ScheduleBlock
for _, scheduleBlock := range scheduleBlocks {
if scheduleBlock.MatchType == matchType {
matchingScheduleBlocks = append(matchingScheduleBlocks, scheduleBlock)
}
}
sort.Slice(matchingScheduleBlocks, func(i, j int) bool {
return matchingScheduleBlocks[i].StartTime.Before(matchingScheduleBlocks[j].StartTime)
})
return matchingScheduleBlocks, nil
}
func (database *Database) DeleteScheduleBlocksByMatchType(matchType string) error {
_, err := database.scheduleBlockMap.Exec("DELETE FROM schedule_blocks WHERE matchtype = ?", matchType)
return err
scheduleBlocks, err := database.GetScheduleBlocksByMatchType(matchType)
if err != nil {
return err
}
for _, scheduleBlock := range scheduleBlocks {
if err = database.scheduleBlockTable.delete(scheduleBlock.Id); err != nil {
return err
}
}
return nil
}
func (database *Database) TruncateScheduleBlocks() error {
return database.scheduleBlockMap.TruncateTables()
return database.scheduleBlockTable.truncate()
}

View File

@@ -5,8 +5,10 @@
package model
import "sort"
type SponsorSlide struct {
Id int
Id int `db:"id"`
Subtitle string
Line1 string
Line2 string
@@ -16,41 +18,45 @@ type SponsorSlide struct {
}
func (database *Database) CreateSponsorSlide(sponsorSlide *SponsorSlide) error {
return database.sponsorSlideMap.Insert(sponsorSlide)
return database.sponsorSlideTable.create(sponsorSlide)
}
func (database *Database) GetSponsorSlideById(id int) (*SponsorSlide, error) {
sponsorSlide := new(SponsorSlide)
err := database.sponsorSlideMap.Get(sponsorSlide, id)
if err != nil && err.Error() == "sql: no rows in result set" {
sponsorSlide = nil
err = nil
}
var sponsorSlide *SponsorSlide
err := database.sponsorSlideTable.getById(id, &sponsorSlide)
return sponsorSlide, err
}
func (database *Database) SaveSponsorSlide(sponsorSlide *SponsorSlide) error {
_, err := database.sponsorSlideMap.Update(sponsorSlide)
return err
func (database *Database) UpdateSponsorSlide(sponsorSlide *SponsorSlide) error {
return database.sponsorSlideTable.update(sponsorSlide)
}
func (database *Database) DeleteSponsorSlide(sponsorSlide *SponsorSlide) error {
_, err := database.sponsorSlideMap.Delete(sponsorSlide)
return err
func (database *Database) DeleteSponsorSlide(id int) error {
return database.sponsorSlideTable.delete(id)
}
func (database *Database) TruncateSponsorSlides() error {
return database.sponsorSlideMap.TruncateTables()
return database.sponsorSlideTable.truncate()
}
func (database *Database) GetAllSponsorSlides() ([]SponsorSlide, error) {
var sponsorSlides []SponsorSlide
err := database.sponsorSlideMap.Select(&sponsorSlides, "SELECT * FROM sponsor_slides ORDER BY displayorder")
return sponsorSlides, err
if err := database.sponsorSlideTable.getAll(&sponsorSlides); err != nil {
return nil, err
}
sort.Slice(sponsorSlides, func(i, j int) bool {
return sponsorSlides[i].DisplayOrder < sponsorSlides[j].DisplayOrder
})
return sponsorSlides, nil
}
func (database *Database) GetNextSponsorSlideDisplayOrder() int {
var count int
_ = database.sponsorSlideMap.SelectOne(&count, "SELECT MAX(displayorder) + 1 FROM sponsor_slides")
return count
sponsorSlides, err := database.GetAllSponsorSlides()
if err != nil {
return 0
}
if len(sponsorSlides) == 0 {
return 1
}
return sponsorSlides[len(sponsorSlides)-1].DisplayOrder + 1
}

View File

@@ -21,22 +21,22 @@ func TestSponsorSlideCrud(t *testing.T) {
db := setupTestDb(t)
defer db.Close()
assert.Equal(t, 0, db.GetNextSponsorSlideDisplayOrder())
assert.Equal(t, 1, db.GetNextSponsorSlideDisplayOrder())
sponsorSlide := SponsorSlide{0, "Subtitle", "Line 1", "Line 2", "", 10, 0}
sponsorSlide := SponsorSlide{0, "Subtitle", "Line 1", "Line 2", "", 10, 1}
assert.Nil(t, db.CreateSponsorSlide(&sponsorSlide))
sponsorSlide2, err := db.GetSponsorSlideById(1)
assert.Nil(t, err)
assert.Equal(t, sponsorSlide, *sponsorSlide2)
assert.Equal(t, 1, db.GetNextSponsorSlideDisplayOrder())
assert.Equal(t, 2, db.GetNextSponsorSlideDisplayOrder())
sponsorSlide.Line1 = "Blorpy"
db.SaveSponsorSlide(&sponsorSlide)
db.UpdateSponsorSlide(&sponsorSlide)
sponsorSlide2, err = db.GetSponsorSlideById(1)
assert.Nil(t, err)
assert.Equal(t, sponsorSlide.Line1, sponsorSlide2.Line1)
db.DeleteSponsorSlide(&sponsorSlide)
db.DeleteSponsorSlide(sponsorSlide.Id)
sponsorSlide2, err = db.GetSponsorSlideById(1)
assert.Nil(t, err)
assert.Nil(t, sponsorSlide2)
@@ -52,5 +52,5 @@ func TestTruncateSponsorSlides(t *testing.T) {
sponsorSlide2, err := db.GetSponsorSlideById(1)
assert.Nil(t, err)
assert.Nil(t, sponsorSlide2)
assert.Equal(t, 0, db.GetNextSponsorSlideDisplayOrder())
assert.Equal(t, 1, db.GetNextSponsorSlideDisplayOrder())
}

View File

@@ -18,7 +18,6 @@ func SetupTestDb(t *testing.T, uniqueName string) *Database {
BaseDir = ".."
dbPath := filepath.Join(BaseDir, fmt.Sprintf("%s_test.db", uniqueName))
os.Remove(dbPath)
os.Remove(dbPath + ".bolt")
database, err := OpenDatabase(dbPath)
assert.Nil(t, err)
return database

View File

@@ -8,31 +8,34 @@ package model
import "time"
type UserSession struct {
Id int
Id int `db:"id"`
Token string
Username string
CreatedAt time.Time
}
func (database *Database) CreateUserSession(session *UserSession) error {
return database.userSessionMap.Insert(session)
return database.userSessionTable.create(session)
}
func (database *Database) GetUserSessionByToken(token string) (*UserSession, error) {
session := new(UserSession)
err := database.userSessionMap.SelectOne(session, "SELECT * FROM user_sessions WHERE token = ?", token)
if err != nil && err.Error() == "sql: no rows in result set" {
session = nil
err = nil
var userSessions []UserSession
if err := database.userSessionTable.getAll(&userSessions); err != nil {
return nil, err
}
return session, err
for _, userSession := range userSessions {
if userSession.Token == token {
return &userSession, nil
}
}
return nil, nil
}
func (database *Database) DeleteUserSession(session *UserSession) error {
_, err := database.userSessionMap.Delete(session)
return err
func (database *Database) DeleteUserSession(id int) error {
return database.userSessionTable.delete(id)
}
func (database *Database) TruncateUserSessions() error {
return database.userSessionMap.TruncateTables()
return database.userSessionTable.truncate()
}

View File

@@ -31,7 +31,7 @@ func TestUserSessionCrud(t *testing.T) {
assert.Equal(t, session.Username, session2.Username)
assert.True(t, session.CreatedAt.Equal(session2.CreatedAt))
db.DeleteUserSession(&session)
db.DeleteUserSession(session.Id)
session2, err = db.GetUserSessionByToken("token1")
assert.Nil(t, err)
assert.Nil(t, session2)