Initial db controllers
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
package main
|
||||
|
||||
import "git.phc.dm.unipi.it/phc/website/database"
|
||||
import (
|
||||
"git.phc.dm.unipi.it/phc/website/database/sqlite"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db, err := database.NewSqlite3Database("phc-server.local.db")
|
||||
db, err := sqlite.New("phc-server.local.db")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
+23
-21
@@ -1,39 +1,41 @@
|
||||
package database
|
||||
|
||||
// Tools
|
||||
|
||||
type DBMigrate interface {
|
||||
Migrate(migrationDir string) error
|
||||
}
|
||||
|
||||
type DBQueryAppunti interface {
|
||||
AllDispenseFile() ([]DispensaFile, error)
|
||||
}
|
||||
|
||||
type DBDispense interface {
|
||||
CreateDispensa(template Dispensa) (string, error)
|
||||
GetDispensa(id string) (Dispensa, error)
|
||||
AllDispense() ([]Dispensa, error)
|
||||
UpdateDispensa(d Dispensa) error
|
||||
DeleteDispensa(id string) error
|
||||
Create(template Dispensa) (string, error)
|
||||
Get(id string) (Dispensa, error)
|
||||
All() ([]Dispensa, error)
|
||||
Update(d Dispensa) error
|
||||
Delete(id string) error
|
||||
}
|
||||
|
||||
type DBUploads interface {
|
||||
CreateUpload(template Upload) (string, error)
|
||||
GetUpload(id string) (Upload, error)
|
||||
Create(template Upload) (string, error)
|
||||
Get(id string) (Upload, error)
|
||||
}
|
||||
|
||||
type DBFileApprovals interface {
|
||||
CreateFileApproval(template FileApproval) (string, error)
|
||||
GetFileApproval(id string) (FileApproval, error)
|
||||
AllFileApprovals() ([]FileApproval, error)
|
||||
UpdateFileApproval(d FileApproval) error
|
||||
DeleteFileApproval(id string) error
|
||||
Create(template FileApproval) (string, error)
|
||||
Get(id string) (FileApproval, error)
|
||||
All() ([]FileApproval, error)
|
||||
Update(d FileApproval) error
|
||||
Delete(id string) error
|
||||
}
|
||||
|
||||
type DBDispensaTags interface {
|
||||
Set(dispensaId string, tags []string) error
|
||||
Get(dispensaId string) ([]string, error)
|
||||
}
|
||||
|
||||
type DBDownloads interface {
|
||||
CreateDownload(template Download) (string, error)
|
||||
}
|
||||
|
||||
type DBTags interface {
|
||||
SetTags(dispensaId string, tags []string) error
|
||||
GetTags(dispensaId string) ([]string, error)
|
||||
Create(template Download) error
|
||||
}
|
||||
|
||||
// DB main "interface group" for interacting with the database, with this technique we can test each "table" api of the DB in isolation.
|
||||
@@ -42,6 +44,6 @@ type DB struct {
|
||||
DBDispense
|
||||
DBUploads
|
||||
DBFileApprovals
|
||||
DBDispensaTags
|
||||
DBDownloads
|
||||
DBTags
|
||||
}
|
||||
|
||||
+10
-1
@@ -8,6 +8,15 @@ type Dispensa struct {
|
||||
Description string `db:"description"`
|
||||
}
|
||||
|
||||
type DispensaFile struct {
|
||||
DispensaId string `db:"dispensa_id"`
|
||||
UploadId string `db:"upload_id"`
|
||||
OwnerId string `db:"owner_id"`
|
||||
Title string `db:"title"`
|
||||
Description string `db:"description"`
|
||||
File string `db:"file"`
|
||||
}
|
||||
|
||||
type Upload struct {
|
||||
Id string `db:"id"`
|
||||
CreatedAt string `db:"created_at"`
|
||||
@@ -21,7 +30,7 @@ type FileApproval struct {
|
||||
CreatedAt string `db:"created_at"`
|
||||
OwnerId string `db:"owner_id"`
|
||||
UploadId string `db:"upload_id"`
|
||||
Status string `db:"status"`
|
||||
Status string `db:"status"` // "approved" | "rejected"
|
||||
}
|
||||
|
||||
type Download struct {
|
||||
|
||||
@@ -1,187 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"git.phc.dm.unipi.it/phc/website/util"
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
type migration struct {
|
||||
Timestamp string
|
||||
Filename string
|
||||
}
|
||||
|
||||
type sqliteDB struct {
|
||||
*sqlx.DB
|
||||
}
|
||||
|
||||
func (db *sqliteDB) Migrate(migrationFolder string) error {
|
||||
log.Printf(`Creating migrations table`)
|
||||
if _, err := db.Exec(`CREATE TABLE IF NOT EXISTS migrations(timestamp TEXT, filename TEXT)`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf(`Loading applied migrations`)
|
||||
appliedMigrations := []migration{}
|
||||
if err := db.Select(&appliedMigrations, `SELECT * FROM migrations`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entries, err := os.ReadDir(migrationFolder)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
return fmt.Errorf("no dirs in migrations folder")
|
||||
}
|
||||
|
||||
if i < len(appliedMigrations) {
|
||||
if appliedMigrations[i].Filename != entry.Name() {
|
||||
return fmt.Errorf("misapplied migration %q with %q", appliedMigrations[i].Filename, entry.Name())
|
||||
}
|
||||
|
||||
log.Printf("Found applied migration %q", entry.Name())
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("Applying new migration %q", entry.Name())
|
||||
|
||||
migrationPath := path.Join(migrationFolder, entry.Name())
|
||||
|
||||
sqlStmts, err := os.ReadFile(migrationPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tx, err := db.Beginx()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(string(sqlStmts)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`INSERT INTO migrations VALUES (?, ?)`,
|
||||
time.Now().Format(time.RFC3339),
|
||||
entry.Name(),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("All migrations applied successfully, database up to date")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *sqliteDB) CreateDispensa(template Dispensa) (string, error) {
|
||||
template.Id = "dispensa/" + util.GenerateRandomString(8)
|
||||
template.CreatedAt = time.Now().Format(time.RFC3339)
|
||||
|
||||
if _, err := db.NamedExec(`
|
||||
INSERT INTO
|
||||
dispense(id, created_at, owner_id, title, description)
|
||||
VALUES
|
||||
(:id, :created_at, :owner_id, :title, :description)
|
||||
`, &template); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return template.Id, nil
|
||||
}
|
||||
|
||||
func (db *sqliteDB) GetDispensa(id string) (Dispensa, error) {
|
||||
var dispensa Dispensa
|
||||
if err := db.Get(&dispensa, `SELECT * FROM dispense WHERE id = ?`, id); err != nil {
|
||||
return Dispensa{}, err
|
||||
}
|
||||
|
||||
return dispensa, nil
|
||||
}
|
||||
|
||||
func (db *sqliteDB) AllDispense() ([]Dispensa, error) {
|
||||
var dispense []Dispensa
|
||||
if err := db.Select(&dispense, `SELECT * FROM dispense`); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dispense, nil
|
||||
}
|
||||
|
||||
func (db *sqliteDB) UpdateDispensa(d Dispensa) error {
|
||||
if _, err := db.NamedExec(`
|
||||
UPDATE
|
||||
dispense
|
||||
SET
|
||||
owner_id = :owner_id
|
||||
title = :title,
|
||||
description = :description
|
||||
WHERE
|
||||
id = :id
|
||||
`, &d); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *sqliteDB) DeleteDispensa(id string) error {
|
||||
panic("TODO: Not implemented")
|
||||
}
|
||||
|
||||
func (db *sqliteDB) CreateUpload(template Upload) (string, error) {
|
||||
template.Id = "upload/" + util.GenerateRandomString(8)
|
||||
|
||||
if _, err := db.NamedExec(`
|
||||
INSERT INTO
|
||||
uploads(id, created_at, owner_id, dispensa_id, file)
|
||||
VALUES
|
||||
(:id, :created_at, :owner_id, :dispensa_id, :file)
|
||||
`, &template); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return template.Id, nil
|
||||
}
|
||||
|
||||
func (db *sqliteDB) GetUpload(id string) (Upload, error) {
|
||||
var upload Upload
|
||||
if err := db.Select(`SELECT * FROM uploads WHERE id = ?`, id); err != nil {
|
||||
return Upload{}, err
|
||||
}
|
||||
|
||||
return upload, nil
|
||||
}
|
||||
|
||||
func NewSqlite3Database(filename string) (*DB, error) {
|
||||
sqlDB, err := sqlx.Open("sqlite3", filename+"?_fk=1")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := sqlDB.Ping(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db := &sqliteDB{sqlDB}
|
||||
|
||||
// in this case the type "sqliteDB" implements all the interfaces declared in "database.DB" so we can pass "db" to all the fields.
|
||||
return &DB{
|
||||
DBMigrate: db,
|
||||
DBDispense: db,
|
||||
DBUploads: db,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"git.phc.dm.unipi.it/phc/website/database"
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
type migration struct {
|
||||
Timestamp string
|
||||
Filename string
|
||||
}
|
||||
|
||||
func New(filename string) (*database.DB, error) {
|
||||
db, err := sqlx.Open("sqlite3", filename+"?_fk=1")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := db.Ping(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &database.DB{
|
||||
DBMigrate: &sqliteDBMigrate{db},
|
||||
DBDispense: &sqliteDBDispense{db},
|
||||
DBUploads: &sqliteDBUploads{db},
|
||||
DBFileApprovals: &sqliteDBFileApprovals{db},
|
||||
DBDownloads: &sqliteDBDownloads{db},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"git.phc.dm.unipi.it/phc/website/database"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type sqliteDBMigrate struct{ *sqlx.DB }
|
||||
|
||||
var _ database.DBMigrate = sqliteDBMigrate{}
|
||||
|
||||
func (db sqliteDBMigrate) Migrate(migrationFolder string) error {
|
||||
log.Printf(`Creating migrations table`)
|
||||
if _, err := db.Exec(`CREATE TABLE IF NOT EXISTS migrations(timestamp TEXT, filename TEXT)`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf(`Loading applied migrations`)
|
||||
appliedMigrations := []migration{}
|
||||
if err := db.Select(&appliedMigrations, `SELECT * FROM migrations`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entries, err := os.ReadDir(migrationFolder)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
return fmt.Errorf("no dirs in migrations folder")
|
||||
}
|
||||
|
||||
if i < len(appliedMigrations) {
|
||||
if appliedMigrations[i].Filename != entry.Name() {
|
||||
return fmt.Errorf("misapplied migration %q with %q", appliedMigrations[i].Filename, entry.Name())
|
||||
}
|
||||
|
||||
log.Printf("Found applied migration %q", entry.Name())
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("Applying new migration %q", entry.Name())
|
||||
|
||||
migrationPath := path.Join(migrationFolder, entry.Name())
|
||||
|
||||
sqlStmts, err := os.ReadFile(migrationPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tx, err := db.Beginx()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(string(sqlStmts)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`INSERT INTO migrations VALUES (?, ?)`,
|
||||
time.Now().Format(time.RFC3339),
|
||||
entry.Name(),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("All migrations applied successfully, database up to date")
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.phc.dm.unipi.it/phc/website/database"
|
||||
"git.phc.dm.unipi.it/phc/website/util"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type sqliteDBDispense struct{ *sqlx.DB }
|
||||
|
||||
var _ database.DBDispense = sqliteDBDispense{}
|
||||
|
||||
func (d sqliteDBDispense) Create(template database.Dispensa) (string, error) {
|
||||
template.Id = "dispensa/" + util.GenerateRandomString(8)
|
||||
template.CreatedAt = time.Now().Format(time.RFC3339)
|
||||
|
||||
if _, err := d.DB.NamedExec(`
|
||||
INSERT INTO
|
||||
dispense(id, created_at, owner_id, title, description)
|
||||
VALUES
|
||||
(:id, :created_at, :owner_id, :title, :description)
|
||||
`, &template); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return template.Id, nil
|
||||
}
|
||||
|
||||
func (d sqliteDBDispense) Get(id string) (database.Dispensa, error) {
|
||||
var dispensa database.Dispensa
|
||||
if err := d.DB.Get(&dispensa, `SELECT * FROM dispense WHERE id = ?`, id); err != nil {
|
||||
return database.Dispensa{}, err
|
||||
}
|
||||
|
||||
return dispensa, nil
|
||||
}
|
||||
|
||||
func (d sqliteDBDispense) All() ([]database.Dispensa, error) {
|
||||
var dispense []database.Dispensa
|
||||
if err := d.DB.Select(&dispense, `SELECT * FROM dispense`); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dispense, nil
|
||||
}
|
||||
|
||||
func (d sqliteDBDispense) Update(dispensa database.Dispensa) error {
|
||||
if _, err := d.DB.NamedExec(`
|
||||
UPDATE
|
||||
dispense
|
||||
SET
|
||||
owner_id = :owner_id
|
||||
title = :title,
|
||||
description = :description
|
||||
WHERE
|
||||
id = :id
|
||||
`, &dispensa); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d sqliteDBDispense) Delete(id string) error {
|
||||
panic("TODO: Not implemented")
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"git.phc.dm.unipi.it/phc/website/database"
|
||||
"git.phc.dm.unipi.it/phc/website/util"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type sqliteDBUploads struct{ *sqlx.DB }
|
||||
|
||||
var _ database.DBUploads = sqliteDBUploads{}
|
||||
|
||||
func (u sqliteDBUploads) Create(template database.Upload) (string, error) {
|
||||
template.Id = "upload/" + util.GenerateRandomString(8)
|
||||
|
||||
if _, err := u.DB.NamedExec(`
|
||||
INSERT INTO
|
||||
uploads(id, created_at, owner_id, dispensa_id, file)
|
||||
VALUES
|
||||
(:id, :created_at, :owner_id, :dispensa_id, :file)
|
||||
`, &template); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return template.Id, nil
|
||||
}
|
||||
|
||||
func (u sqliteDBUploads) Get(id string) (database.Upload, error) {
|
||||
var upload database.Upload
|
||||
if err := u.DB.Select(`SELECT * FROM uploads WHERE id = ?`, id); err != nil {
|
||||
return database.Upload{}, err
|
||||
}
|
||||
|
||||
return upload, nil
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"git.phc.dm.unipi.it/phc/website/database"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type sqliteDBFileApprovals struct{ *sqlx.DB }
|
||||
|
||||
func (f sqliteDBFileApprovals) Create(template database.FileApproval) (string, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (f sqliteDBFileApprovals) Get(id string) (database.FileApproval, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (f sqliteDBFileApprovals) All() ([]database.FileApproval, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (f sqliteDBFileApprovals) Update(d database.FileApproval) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (f sqliteDBFileApprovals) Delete(id string) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"git.phc.dm.unipi.it/phc/website/database"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type sqliteDBDownloads struct{ *sqlx.DB }
|
||||
|
||||
var _ database.DBDownloads = sqliteDBDownloads{}
|
||||
|
||||
func (d sqliteDBDownloads) Create(template database.Download) error {
|
||||
if _, err := d.DB.NamedExec(`
|
||||
INSERT INTO
|
||||
downloads(dispensa_id, timestamp)
|
||||
VALUES
|
||||
(:dispensa_id, :timestamp)
|
||||
`, &template); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user