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

View File

@@ -9,6 +9,7 @@ import (
"log/slog"
"net/http"
"forge.capytal.company/capytalcode/project-comicverse/database"
"forge.capytal.company/capytalcode/project-comicverse/router"
"forge.capytal.company/capytalcode/project-comicverse/service"
"forge.capytal.company/capytalcode/project-comicverse/static"
@@ -111,9 +112,19 @@ func (app *app) setup() error {
var err error
database, err := database.New(database.Config{
SQL: app.db,
Context: app.ctx,
Assertions: app.assert,
Logger: app.logger,
})
if err != nil {
return errors.Join(errors.New("unable to create database struct"), err)
}
service, err := service.New(service.Config{
DB: app.db,
S3: app.s3,
DB: database,
Context: app.ctx,

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
}

1
go.mod
View File

@@ -8,6 +8,7 @@ require (
forge.capytal.company/loreddev/x v0.0.0-20250305165122-0ccb26ab783b
github.com/aws/aws-sdk-go-v2 v1.36.3
github.com/aws/aws-sdk-go-v2/service/s3 v1.78.1
github.com/google/uuid v1.6.0
github.com/tursodatabase/go-libsql v0.0.0-20241221181756-6121e81fbf92
)

2
go.sum
View File

@@ -26,6 +26,8 @@ github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/libsql/sqlite-antlr4-parser v0.0.0-20240327125255-dbf53b6cbf06 h1:JLvn7D+wXjH9g4Jsjo+VqmzTUpl/LX7vfr6VOfSWTdM=
github.com/libsql/sqlite-antlr4-parser v0.0.0-20240327125255-dbf53b6cbf06/go.mod h1:FUkZ5OHjlGPjnM2UyGJz9TypXQFgYqw6AFNO1UiROTM=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=

View File

@@ -1,6 +1,32 @@
package service
func (s *service) NewProject() {
import (
"encoding/xml"
"errors"
"forge.capytal.company/capytalcode/project-comicverse/database"
)
func (s *service) NewProject() error {
s.assert.NotNil(s.db)
id, err := uuid.NewV7()
if err != nil {
return err
}
s.assert.NotZero(id.String(), "UUID should never be invalid")
err = s.db.Insert(&database.Project{
ID: id.String(),
Title: "New Project",
})
if err != nil {
return errors.Join(errors.New("database returned error while inserting new project"), err)
}
return nil
}
func (s *service) ListProjects() {

View File

@@ -2,17 +2,17 @@ package service
import (
"context"
"database/sql"
"errors"
"log/slog"
"forge.capytal.company/capytalcode/project-comicverse/database"
"forge.capytal.company/loreddev/x/tinyssert"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
type service struct {
db *sql.DB
s3 *s3.Client
db *database.Database
ctx context.Context
@@ -22,7 +22,7 @@ type service struct {
func New(cfg Config) (Service, error) {
if cfg.DB == nil {
return nil, errors.New("database should not be a nil interface")
return nil, errors.New("database should not be a nil pointer")
}
if cfg.S3 == nil {
return nil, errors.New("s3 client should not be a nil pointer")
@@ -47,8 +47,8 @@ func New(cfg Config) (Service, error) {
}
type Config struct {
DB *sql.DB
S3 *s3.Client
DB *database.Database
Context context.Context