Initial db controllers

This commit is contained in:
2022-09-26 20:14:54 +02:00
parent 9df45cf83a
commit c7e33c8e72
11 changed files with 327 additions and 211 deletions
+4 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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 {
-187
View File
@@ -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
}
+31
View File
@@ -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
}
+82
View File
@@ -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
}
+68
View File
@@ -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")
}
+35
View File
@@ -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
}
+28
View File
@@ -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
}
+23
View File
@@ -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
}