feat(service,database): new Database abstraction to initiate and manipulate database

This commit is contained in:
Guz
2025-03-11 14:19:21 -03:00
parent 1c608b30be
commit fca5ad29b9
7 changed files with 199 additions and 6 deletions

115
database/database.go Normal file
View File

@@ -0,0 +1,115 @@
package database
import (
"context"
"database/sql"
"errors"
"fmt"
"log/slog"
"forge.capytal.company/loreddev/x/tinyssert"
)
type Database struct {
sql *sql.DB
ctx context.Context
assert tinyssert.Assertions
log *slog.Logger
}
func New(cfg Config) (*Database, error) {
if cfg.SQL == nil {
return nil, errors.New("SQL database interface should not be nil")
}
if cfg.Context == nil {
return nil, errors.New("context interface should not be nil")
}
if cfg.Assertions == nil {
return nil, errors.New("assertions interface should not be nil")
}
if cfg.Logger == nil {
return nil, errors.New("logger should not be a nil pointer")
}
db := &Database{
sql: cfg.SQL,
ctx: cfg.Context,
assert: cfg.Assertions,
log: cfg.Logger,
}
if err := db.setup(); err != nil {
return nil, errors.New("error while setting up Database struct")
}
return db, nil
}
type Config struct {
SQL *sql.DB
Context context.Context
Assertions tinyssert.Assertions
Logger *slog.Logger
}
func (db *Database) setup() error {
db.assert.NotNil(db.sql)
db.assert.NotNil(db.ctx)
db.assert.NotNil(db.log)
log := db.log
log.Info("Setting up database")
log.Debug("Pinging database")
err := db.sql.PingContext(db.ctx)
if err != nil {
return errors.Join(errors.New("unable to ping database"), err)
}
log.Debug("Creating tables")
tables := []Table{
&Project{},
}
tx, err := db.sql.BeginTx(db.ctx, nil)
if err != nil {
return errors.Join(errors.New("unable to start transaction to create tables"), err)
}
for _, t := range tables {
_, err := tx.Exec(t.setup())
if err != nil {
return errors.Join(fmt.Errorf("error while trying to create table %T", t), err)
}
}
err = tx.Commit()
if err != nil {
return errors.Join(errors.New("unable to run transaction to create tables"), err)
}
return nil
}
func (db *Database) Insert(t Table) error {
q, err := t.insert()
if err != nil {
return errors.Join(fmt.Errorf("creating query to insert table %T resulted in error", t), err)
}
_, err = db.sql.ExecContext(db.ctx, q)
if err != nil {
return err
}
return nil
}
type Table interface {
setup() string
insert() (string, error)
}

38
database/projects.go Normal file
View File

@@ -0,0 +1,38 @@
package database
import (
"errors"
"fmt"
)
type Project struct {
ID string
Title string
}
var _ Table = (*Project)(nil)
func (p *Project) setup() string {
return `
CREATE TABLE IF NOT EXISTS projects (
id TEXT PRIMARY KEY NOT NULL,
title TEXT NOT NULL
) STRICT`
}
func (p *Project) insert() (string, error) {
if p.ID == "" {
return "", errors.New("ID field shouldn't be a empty string")
}
if p.Title == "" {
return "", errors.New("Title field shouldn't be a empty string")
}
return fmt.Sprintf(`
INSERT OR FAIL INTO projects (
id,
title
) VALUES (
'%s',
'%s'
)`, p.ID, p.Title), nil
}