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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
sq "github.com/Masterminds/squirrel"
|
sq "github.com/Masterminds/squirrel"
|
||||||
"github.com/abelith/spellbook/internal/errors"
|
"github.com/abelith/spellbook/internal/errors"
|
||||||
"github.com/abelith/spellbook/internal/models"
|
"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 {
|
func (r *Repo) Add(ctx context.Context, spell models.Spell) error {
|
||||||
tags := strings.Join(spell.Tags, ",")
|
if _, err := insertSpell(ctx, r.db, spell); err != nil {
|
||||||
|
|
||||||
_, 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 {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,6 +142,97 @@ func (r *Repo) GetMany(ctx context.Context, query models.SearchQuery) ([]models.
|
|||||||
return spells, nil
|
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) {
|
func scanSpell(row scanner) (models.Spell, error) {
|
||||||
spell := models.Spell{}
|
spell := models.Spell{}
|
||||||
tagsString := ""
|
tagsString := ""
|
||||||
@@ -162,3 +247,14 @@ func scanSpell(row scanner) (models.Spell, error) {
|
|||||||
|
|
||||||
return spell, nil
|
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 {
|
type SpellCommand interface {
|
||||||
Add(ctx context.Context, spell models.Spell) error
|
Add(ctx context.Context, spell models.Spell) error
|
||||||
Delete(ctx context.Context, spellName string) error
|
Delete(ctx context.Context, spellName string) error
|
||||||
|
Import(ctx context.Context, ch <-chan models.Spell) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type SpellQuery interface {
|
type SpellQuery interface {
|
||||||
Get(ctx context.Context, spellName string) (models.Spell, error)
|
Get(ctx context.Context, spellName string) (models.Spell, error)
|
||||||
GetMany(ctx context.Context, query models.SearchQuery) ([]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 {
|
type Application struct {
|
||||||
|
|||||||
Reference in new issue
Block a user