feat: added repo export/import streaming methods
This commit is contained in:
1 parent
c5465fe6f6
commit
4de3218c40
2 files changed
+108
-9
No files matched your search
@@ -3,6 +3,7 @@ package sqlite
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"github.com/abelith/spellbook/internal/errors"
|
||||
"github.com/abelith/spellbook/internal/models"
|
||||
@@ -45,17 +46,10 @@ func (r *Repo) Init(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (r *Repo) Add(ctx context.Context, spell models.Spell) error {
|
||||
tags := strings.Join(spell.Tags, ",")
|
||||
|
||||
_, err := sq.Insert("spells").
|
||||
Columns("name", "spell", "short_desc", "description", "tags").
|
||||
Values(spell.Name, spell.Spell, spell.ShortDesc, spell.Description, tags).
|
||||
PlaceholderFormat(sq.Question).
|
||||
RunWith(r.db).
|
||||
ExecContext(ctx)
|
||||
if err != nil {
|
||||
if _, err := insertSpell(ctx, r.db, spell); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -148,6 +142,97 @@ func (r *Repo) GetMany(ctx context.Context, query models.SearchQuery) ([]models.
|
||||
return spells, nil
|
||||
}
|
||||
|
||||
func (r *Repo) Import(ctx context.Context, ch <-chan models.Spell) error {
|
||||
return r.runTx(ctx, func(tx *sql.Tx) error {
|
||||
cache := sq.NewStmtCache(tx)
|
||||
defer cache.Clear()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case spell, ok := <-ch:
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err := insertSpell(ctx, cache, spell)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Repo) Count(ctx context.Context) (int, error) {
|
||||
var count int
|
||||
err := sq.Select("COUNT(*)").
|
||||
From("spells").
|
||||
RunWith(r.db).
|
||||
ScanContext(ctx, &count)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (r *Repo) Export(ctx context.Context) (<-chan models.Spell, <-chan error, error) {
|
||||
ch := make(chan models.Spell, 1)
|
||||
errCh := make(chan error, 1)
|
||||
rows, err := sq.Select("id", "name", "spell", "short_desc", "description", "tags").
|
||||
From("spells").
|
||||
RunWith(r.db).
|
||||
QueryContext(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer close(errCh)
|
||||
defer close(ch)
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
spell, err := scanSpell(rows)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
errCh <- ctx.Err()
|
||||
return
|
||||
case ch <- spell:
|
||||
}
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
errCh <- err
|
||||
}
|
||||
}()
|
||||
|
||||
return ch, errCh, nil
|
||||
}
|
||||
|
||||
func (r *Repo) runTx(ctx context.Context, f func(tx *sql.Tx) error) error {
|
||||
tx, err := r.db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to begin tx: %w", err)
|
||||
}
|
||||
|
||||
if err := f(tx); err != nil {
|
||||
if rbErr := tx.Rollback(); rbErr != nil {
|
||||
return fmt.Errorf("err: %v, rollback err: %w", err, rbErr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func scanSpell(row scanner) (models.Spell, error) {
|
||||
spell := models.Spell{}
|
||||
tagsString := ""
|
||||
@@ -162,3 +247,14 @@ func scanSpell(row scanner) (models.Spell, error) {
|
||||
|
||||
return spell, nil
|
||||
}
|
||||
|
||||
func insertSpell(ctx context.Context, runner sq.BaseRunner, spell models.Spell) (sql.Result, error) {
|
||||
tags := strings.Join(spell.Tags, ",")
|
||||
|
||||
return sq.Insert("spells").
|
||||
Columns("name", "spell", "short_desc", "description", "tags").
|
||||
Values(spell.Name, spell.Spell, spell.ShortDesc, spell.Description, tags).
|
||||
PlaceholderFormat(sq.Question).
|
||||
RunWith(runner).
|
||||
ExecContext(ctx)
|
||||
}
|
||||
@@ -12,11 +12,14 @@ type SpellCaster interface {
|
||||
type SpellCommand interface {
|
||||
Add(ctx context.Context, spell models.Spell) error
|
||||
Delete(ctx context.Context, spellName string) error
|
||||
Import(ctx context.Context, ch <-chan models.Spell) error
|
||||
}
|
||||
|
||||
type SpellQuery interface {
|
||||
Get(ctx context.Context, spellName string) (models.Spell, error)
|
||||
GetMany(ctx context.Context, query models.SearchQuery) ([]models.Spell, error)
|
||||
Count(ctx context.Context) (int, error)
|
||||
Export(ctx context.Context) (<-chan models.Spell, <-chan error, error)
|
||||
}
|
||||
|
||||
type Application struct {
|
||||
|
||||
Reference in new issue
Block a user