wip
This commit is contained in:
parent
18d81c545d
commit
e1d7eb0a13
|
@ -44,14 +44,14 @@ var AddCmd = &cli.Command{
|
|||
},
|
||||
}
|
||||
|
||||
func NewAddCmd(_ storage.LocalIDRepo, eventRepo storage.EventRepo) *cli.Command {
|
||||
func NewAddCmd(localRepo storage.LocalID, eventRepo storage.Event) *cli.Command {
|
||||
AddCmd.Action = func(cCtx *cli.Context) error {
|
||||
return Add(cCtx.String("name"), cCtx.String("on"), cCtx.String("at"), cCtx.String("for"), eventRepo)
|
||||
return Add(localRepo, eventRepo, cCtx.String("name"), cCtx.String("on"), cCtx.String("at"), cCtx.String("for"))
|
||||
}
|
||||
return AddCmd
|
||||
}
|
||||
|
||||
func Add(nameStr, onStr, atStr, frStr string, repo storage.EventRepo) error {
|
||||
func Add(localIDRepo storage.LocalID, eventRepo storage.Event, nameStr, onStr, atStr, frStr string) error {
|
||||
if nameStr == "" {
|
||||
return fmt.Errorf("%w: name is required", ErrInvalidArg)
|
||||
}
|
||||
|
@ -91,9 +91,17 @@ func Add(nameStr, onStr, atStr, frStr string, repo storage.EventRepo) error {
|
|||
}
|
||||
e.Duration = fr
|
||||
}
|
||||
if err := repo.Store(e); err != nil {
|
||||
if err := eventRepo.Store(e); err != nil {
|
||||
return fmt.Errorf("could not store event: %v", err)
|
||||
}
|
||||
|
||||
localID, err := localIDRepo.Next()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create next local id: %v", err)
|
||||
}
|
||||
if err := localIDRepo.Store(e.ID, localID); err != nil {
|
||||
return fmt.Errorf("could not store local id: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"github.com/google/go-cmp/cmp"
|
||||
"go-mod.ewintr.nl/planner/item"
|
||||
"go-mod.ewintr.nl/planner/plan/command"
|
||||
"go-mod.ewintr.nl/planner/plan/storage"
|
||||
"go-mod.ewintr.nl/planner/plan/storage/memory"
|
||||
)
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
|
@ -104,21 +104,34 @@ func TestAdd(t *testing.T) {
|
|||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mem := storage.NewMemory()
|
||||
actErr := command.Add(tc.args["name"], tc.args["on"], tc.args["at"], tc.args["for"], mem) != nil
|
||||
eventRepo := memory.NewEvent()
|
||||
localRepo := memory.NewLocalID()
|
||||
actErr := command.Add(localRepo, eventRepo, tc.args["name"], tc.args["on"], tc.args["at"], tc.args["for"]) != nil
|
||||
if tc.expErr != actErr {
|
||||
t.Errorf("exp %v, got %v", tc.expErr, actErr)
|
||||
}
|
||||
if tc.expErr {
|
||||
return
|
||||
}
|
||||
actEvents, err := mem.FindAll()
|
||||
actEvents, err := eventRepo.FindAll()
|
||||
if err != nil {
|
||||
t.Errorf("exp nil, got %v", err)
|
||||
}
|
||||
if len(actEvents) != 1 {
|
||||
t.Errorf("exp 1, got %d", len(actEvents))
|
||||
}
|
||||
|
||||
actLocalIDs, err := localRepo.FindAll()
|
||||
if err != nil {
|
||||
t.Errorf("exp nil, got %v", err)
|
||||
}
|
||||
if len(actLocalIDs) != 1 {
|
||||
t.Errorf("exp 1, got %v", len(actLocalIDs))
|
||||
}
|
||||
if _, ok := actLocalIDs[actEvents[0].ID]; !ok {
|
||||
t.Errorf("exp true, got %v", ok)
|
||||
}
|
||||
|
||||
if actEvents[0].ID == "" {
|
||||
t.Errorf("exp string not te be empty")
|
||||
}
|
||||
|
|
|
@ -13,14 +13,15 @@ var ListCmd = &cli.Command{
|
|||
Usage: "List everything",
|
||||
}
|
||||
|
||||
func NewListCmd(_ storage.LocalIDRepo, repo storage.EventRepo) *cli.Command {
|
||||
ListCmd.Action = NewListAction(repo)
|
||||
func NewListCmd(localRepo storage.LocalID, eventRepo storage.Event) *cli.Command {
|
||||
ListCmd.Action = func(cCtx *cli.Context) error {
|
||||
return List(localRepo, eventRepo)
|
||||
}
|
||||
return ListCmd
|
||||
}
|
||||
|
||||
func NewListAction(repo storage.EventRepo) func(*cli.Context) error {
|
||||
return func(cCtx *cli.Context) error {
|
||||
all, err := repo.FindAll()
|
||||
func List(localRepo storage.LocalID, eventRepo storage.Event) error {
|
||||
all, err := eventRepo.FindAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -30,5 +31,3 @@ func NewListAction(repo storage.EventRepo) func(*cli.Context) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
|
||||
"github.com/urfave/cli/v2"
|
||||
"go-mod.ewintr.nl/planner/plan/command"
|
||||
"go-mod.ewintr.nl/planner/plan/storage"
|
||||
"go-mod.ewintr.nl/planner/plan/storage/sqlite"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
|
@ -23,7 +23,7 @@ func main() {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
localIDRepo, eventRepo, err := storage.NewSqlites(conf.DBPath)
|
||||
localIDRepo, eventRepo, err := sqlite.NewSqlites(conf.DBPath)
|
||||
if err != nil {
|
||||
fmt.Printf("could not open db file: %s\n", err)
|
||||
os.Exit(1)
|
||||
|
|
|
@ -8,18 +8,18 @@ import (
|
|||
"go-mod.ewintr.nl/planner/item"
|
||||
)
|
||||
|
||||
type MemoryEvent struct {
|
||||
type Event struct {
|
||||
events map[string]item.Event
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
func NewMemoryEvent() *MemoryEvent {
|
||||
return &MemoryEvent{
|
||||
func NewEvent() *Event {
|
||||
return &Event{
|
||||
events: make(map[string]item.Event),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *MemoryEvent) Find(id string) (item.Event, error) {
|
||||
func (r *Event) Find(id string) (item.Event, error) {
|
||||
r.mutex.RLock()
|
||||
defer r.mutex.RUnlock()
|
||||
|
||||
|
@ -30,7 +30,7 @@ func (r *MemoryEvent) Find(id string) (item.Event, error) {
|
|||
return event, nil
|
||||
}
|
||||
|
||||
func (r *MemoryEvent) FindAll() ([]item.Event, error) {
|
||||
func (r *Event) FindAll() ([]item.Event, error) {
|
||||
r.mutex.RLock()
|
||||
defer r.mutex.RUnlock()
|
||||
|
||||
|
@ -45,7 +45,7 @@ func (r *MemoryEvent) FindAll() ([]item.Event, error) {
|
|||
return events, nil
|
||||
}
|
||||
|
||||
func (r *MemoryEvent) Store(e item.Event) error {
|
||||
func (r *Event) Store(e item.Event) error {
|
||||
r.mutex.Lock()
|
||||
defer r.mutex.Unlock()
|
||||
|
||||
|
@ -54,7 +54,7 @@ func (r *MemoryEvent) Store(e item.Event) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (r *MemoryEvent) Delete(id string) error {
|
||||
func (r *Event) Delete(id string) error {
|
||||
r.mutex.Lock()
|
||||
defer r.mutex.Unlock()
|
||||
|
||||
|
|
|
@ -7,10 +7,10 @@ import (
|
|||
"go-mod.ewintr.nl/planner/item"
|
||||
)
|
||||
|
||||
func TestMemory(t *testing.T) {
|
||||
func TestEvent(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mem := NewMemory()
|
||||
mem := NewEvent()
|
||||
|
||||
t.Log("empty")
|
||||
actEvents, actErr := mem.FindAll()
|
||||
|
|
|
@ -3,34 +3,31 @@ package memory
|
|||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"go-mod.ewintr.nl/planner/plan/storage"
|
||||
)
|
||||
|
||||
type MemoryLocalID struct {
|
||||
type LocalID struct {
|
||||
ids map[string]int
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
func NewMemoryLocalID() *MemoryLocalID {
|
||||
return &MemoryLocalID{
|
||||
func NewLocalID() *LocalID {
|
||||
return &LocalID{
|
||||
ids: make(map[string]int),
|
||||
}
|
||||
}
|
||||
|
||||
func (ml *MemoryLocalID) FindAll() (map[string]int, error) {
|
||||
func (ml *LocalID) FindAll() (map[string]int, error) {
|
||||
ml.mutex.RLock()
|
||||
defer ml.mutex.RUnlock()
|
||||
|
||||
return ml.ids, nil
|
||||
}
|
||||
|
||||
func (ml *MemoryLocalID) Next() (string, int, error) {
|
||||
func (ml *LocalID) Next() (int, error) {
|
||||
ml.mutex.RLock()
|
||||
defer ml.mutex.RUnlock()
|
||||
|
||||
id := uuid.New().String()
|
||||
|
||||
cur := make([]int, 0, len(ml.ids))
|
||||
for _, i := range ml.ids {
|
||||
cur = append(cur, i)
|
||||
|
@ -38,10 +35,10 @@ func (ml *MemoryLocalID) Next() (string, int, error) {
|
|||
|
||||
localID := storage.NextLocalID(cur)
|
||||
|
||||
return id, localID, nil
|
||||
return localID, nil
|
||||
}
|
||||
|
||||
func (ml *MemoryLocalID) Store(id string, localID int) error {
|
||||
func (ml *LocalID) Store(id string, localID int) error {
|
||||
ml.mutex.Lock()
|
||||
defer ml.mutex.Unlock()
|
||||
|
||||
|
@ -50,12 +47,12 @@ func (ml *MemoryLocalID) Store(id string, localID int) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ml *MemoryLocalID) Delete(id string) error {
|
||||
func (ml *LocalID) Delete(id string) error {
|
||||
ml.mutex.Lock()
|
||||
defer ml.mutex.Unlock()
|
||||
|
||||
if _, ok := ml.ids[id]; !ok {
|
||||
return ErrNotFound
|
||||
return storage.ErrNotFound
|
||||
}
|
||||
|
||||
delete(ml.ids, id)
|
|
@ -0,0 +1,68 @@
|
|||
package memory_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"go-mod.ewintr.nl/planner/plan/storage"
|
||||
"go-mod.ewintr.nl/planner/plan/storage/memory"
|
||||
)
|
||||
|
||||
func TestLocalID(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
repo := memory.NewLocalID()
|
||||
|
||||
t.Log("start empty")
|
||||
actIDs, actErr := repo.FindAll()
|
||||
if actErr != nil {
|
||||
t.Errorf("exp nil, got %v", actErr)
|
||||
}
|
||||
if len(actIDs) != 0 {
|
||||
t.Errorf("exp nil, got %v", actErr)
|
||||
}
|
||||
|
||||
t.Log("next id")
|
||||
actNext, actErr := repo.Next()
|
||||
if actErr != nil {
|
||||
t.Errorf("exp nil, got %v", actErr)
|
||||
}
|
||||
if actNext != 1 {
|
||||
t.Errorf("exp 1, got %v", actNext)
|
||||
}
|
||||
|
||||
t.Log("store")
|
||||
if actErr = repo.Store("test", 1); actErr != nil {
|
||||
t.Errorf("exp nil, got %v", actErr)
|
||||
}
|
||||
|
||||
actIDs, actErr = repo.FindAll()
|
||||
if actErr != nil {
|
||||
t.Errorf("exp nil, got %v", actErr)
|
||||
}
|
||||
expIDs := map[string]int{
|
||||
"test": 1,
|
||||
}
|
||||
if diff := cmp.Diff(expIDs, actIDs); diff != "" {
|
||||
t.Errorf("(exp +, got -)\n%s", diff)
|
||||
}
|
||||
|
||||
t.Log("delete")
|
||||
if actErr = repo.Delete("test"); actErr != nil {
|
||||
t.Errorf("exp nil, got %v", actErr)
|
||||
}
|
||||
actIDs, actErr = repo.FindAll()
|
||||
if actErr != nil {
|
||||
t.Errorf("exp nil, got %v", actErr)
|
||||
}
|
||||
if len(actIDs) != 0 {
|
||||
t.Errorf("exp 0, got %v", actErr)
|
||||
}
|
||||
|
||||
t.Log("delete non-existing")
|
||||
actErr = repo.Delete("non-existing")
|
||||
if !errors.Is(actErr, storage.ErrNotFound) {
|
||||
t.Errorf("exp %v, got %v", storage.ErrNotFound, actErr)
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import (
|
|||
"time"
|
||||
|
||||
"go-mod.ewintr.nl/planner/item"
|
||||
"go-mod.ewintr.nl/planner/plan/storage"
|
||||
)
|
||||
|
||||
type SqliteEvent struct {
|
||||
|
@ -59,7 +60,6 @@ FROM events`)
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err)
|
||||
}
|
||||
|
||||
result := make([]item.Event, 0)
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
|
@ -93,7 +93,7 @@ WHERE id = ?`, id)
|
|||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return fmt.Errorf("event not found: %s", id)
|
||||
return storage.ErrNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
package sqlite
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"go-mod.ewintr.nl/planner/plan/storage"
|
||||
)
|
||||
|
||||
type LocalID struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func (l *LocalID) FindAll() (map[string]int, error) {
|
||||
rows, err := l.db.Query(`
|
||||
SELECT id, local_id
|
||||
FROM localids
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err)
|
||||
}
|
||||
result := make(map[string]int)
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var id string
|
||||
var localID int
|
||||
if err := rows.Scan(&id, &localID); err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err)
|
||||
}
|
||||
result[id] = localID
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (l *LocalID) Next() (int, error) {
|
||||
idMap, err := l.FindAll()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cur := make([]int, 0, len(idMap))
|
||||
for _, localID := range idMap {
|
||||
cur = append(cur, localID)
|
||||
}
|
||||
|
||||
return storage.NextLocalID(cur), nil
|
||||
}
|
||||
|
||||
func (l *LocalID) Store(id string, localID int) error {
|
||||
if _, err := l.db.Exec(`
|
||||
INSERT INTO localids
|
||||
(id, local_id)
|
||||
VALUES
|
||||
(? ,?)`, id, localID); err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrSqliteFailure, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LocalID) Delete(id string) error {
|
||||
result, err := l.db.Exec(`
|
||||
DELETE FROM localids
|
||||
WHERE id = ?`, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrSqliteFailure, err)
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrSqliteFailure, err)
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
return storage.ErrNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -27,33 +27,29 @@ var (
|
|||
ErrSqliteFailure = errors.New("sqlite returned an error")
|
||||
)
|
||||
|
||||
type SqliteLocal struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewSqlites(dbPath string) (*SqliteLocal, *SqliteEvent, error) {
|
||||
func NewSqlites(dbPath string) (*LocalID, *SqliteEvent, error) {
|
||||
db, err := sql.Open("sqlite", dbPath)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("%w: %v", ErrInvalidConfiguration, err)
|
||||
}
|
||||
|
||||
sl := &SqliteLocal{
|
||||
sl := &LocalID{
|
||||
db: db,
|
||||
}
|
||||
se := &SqliteEvent{
|
||||
db: db,
|
||||
}
|
||||
|
||||
if err := sl.migrate(migrations); err != nil {
|
||||
if err := migrate(db, migrations); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return sl, se, nil
|
||||
}
|
||||
|
||||
func (s *SqliteLocal) migrate(wanted []string) error {
|
||||
func migrate(db *sql.DB, wanted []string) error {
|
||||
// admin table
|
||||
if _, err := s.db.Exec(`
|
||||
if _, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS migration
|
||||
("id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "query" TEXT)
|
||||
`); err != nil {
|
||||
|
@ -61,7 +57,7 @@ CREATE TABLE IF NOT EXISTS migration
|
|||
}
|
||||
|
||||
// find existing
|
||||
rows, err := s.db.Query(`SELECT query FROM migration ORDER BY id`)
|
||||
rows, err := db.Query(`SELECT query FROM migration ORDER BY id`)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrSqliteFailure, err)
|
||||
}
|
||||
|
@ -84,12 +80,12 @@ CREATE TABLE IF NOT EXISTS migration
|
|||
|
||||
// execute missing
|
||||
for _, query := range missing {
|
||||
if _, err := s.db.Exec(string(query)); err != nil {
|
||||
if _, err := db.Exec(string(query)); err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrSqliteFailure, err)
|
||||
}
|
||||
|
||||
// register
|
||||
if _, err := s.db.Exec(`
|
||||
if _, err := db.Exec(`
|
||||
INSERT INTO migration
|
||||
(query) VALUES (?)
|
||||
`, query); err != nil {
|
||||
|
|
|
@ -11,14 +11,14 @@ var (
|
|||
ErrNotFound = errors.New("not found")
|
||||
)
|
||||
|
||||
type LocalIDRepo interface {
|
||||
type LocalID interface {
|
||||
FindAll() (map[string]int, error)
|
||||
Next() (string, int, error)
|
||||
Next() (int, error)
|
||||
Store(id string, localID int) error
|
||||
Delete(id string) error
|
||||
}
|
||||
|
||||
type EventRepo interface {
|
||||
type Event interface {
|
||||
Store(event item.Event) error
|
||||
Find(id string) (item.Event, error)
|
||||
FindAll() ([]item.Event, error)
|
||||
|
|
Loading…
Reference in New Issue