diff --git a/core/handlers/create_note.go b/core/handlers/create_note.go index d1b462a..8e3361b 100644 --- a/core/handlers/create_note.go +++ b/core/handlers/create_note.go @@ -2,6 +2,7 @@ package handlers import ( "bytes" + "context" "encoding/json" "os" @@ -18,11 +19,12 @@ type createNoteCommand struct { } type CreateNoteHandler struct { + uow repositories.UnitOfWork noteRepo repositories.NoteRepository tagRepo repositories.TagRepository } -func (h CreateNoteHandler) Handle(params []byte) (*rpc.Response, *rpc.Error) { +func (h CreateNoteHandler) Handle(ctx context.Context, params []byte) (*rpc.Response, *rpc.Error) { var cmd createNoteCommand if err := json.Unmarshal(params, &cmd); err != nil { diff --git a/core/services/note_service.go b/core/services/note_service.go new file mode 100644 index 0000000..e69de29 diff --git a/persistence/repositories/note_repository.go b/persistence/repositories/note_repository.go index 6832f41..0c858b7 100644 --- a/persistence/repositories/note_repository.go +++ b/persistence/repositories/note_repository.go @@ -1,21 +1,22 @@ package repositories -import "database/sql" +import ( + "context" +) type NoteRepository interface { - Upsert(title string, path string, slug string) error + Upsert(ctx context.Context, title string, path string, slug string) error } type noteRepository struct { - db *sql.DB + dbCtx DBContext } -func NewNoteRepository(db *sql.DB) NoteRepository { - return ¬eRepository{db: db} +func NewNoteRepository(dbContext DBContext) NoteRepository { + return ¬eRepository{dbCtx: dbContext} } - -func (r *noteRepository) Upsert(title string, path string, slug string) error { +func (r *noteRepository) Upsert(ctx context.Context, title string, path string, slug string) error { query := ` INSERT INTO notes (title, path, slug) VALUES ($1, $2, $3) @@ -23,6 +24,6 @@ func (r *noteRepository) Upsert(title string, path string, slug string) error { SET title = EXCLUDED.title, path = EXCLUDED.path; ` - _, err := r.db.Exec(query, title, path, slug) + _, err := r.dbCtx.ExecContext(ctx, query, title, path, slug) return err } diff --git a/persistence/repositories/tag_repository.go b/persistence/repositories/tag_repository.go index 0906a5f..3f8b950 100644 --- a/persistence/repositories/tag_repository.go +++ b/persistence/repositories/tag_repository.go @@ -1,33 +1,27 @@ package repositories import ( - "database/sql" + "context" "strings" ) type TagRepository interface { - Upsert(names []string) error + Upsert(ctx context.Context, names []string) error } type tagRepository struct { - db *sql.DB + dbContext DBContext } -func NewTagRepository(db *sql.DB) TagRepository { - return &tagRepository{db: db} +func NewTagRepository(dbContext DBContext) TagRepository { + return &tagRepository{dbContext: dbContext} } -func (r *tagRepository) Upsert(names []string) error { +func (r *tagRepository) Upsert(ctx context.Context, names []string) error { if len(names) == 0 { return nil } - tx, err := r.db.Begin() - if err != nil { - return err - } - defer tx.Rollback() - placeholders := make([]string, 0, len(names)) args := make([]any, 0, len(names)) @@ -42,11 +36,12 @@ func (r *tagRepository) Upsert(names []string) error { "SELECT name FROM input " + "ON CONFLICT(name) DO NOTHING;" - if _, err := tx.Exec(query, args...); err != nil { + _, err := r.dbContext.ExecContext(ctx, query, args) + if err != nil { return err } - return tx.Commit() + return nil } func (r *tagRepository) UpsertNoteTags(noteID int64, tagIDs []int64) error { @@ -54,12 +49,6 @@ func (r *tagRepository) UpsertNoteTags(noteID int64, tagIDs []int64) error { return nil } - tx, err := r.db.Begin() - if err != nil { - return err - } - defer tx.Rollback() - placeholders := make([]string, 0, len(tagIDs)) args := make([]any, 0, len(tagIDs)) @@ -75,9 +64,10 @@ func (r *tagRepository) UpsertNoteTags(noteID int64, tagIDs []int64) error { "ON CONFLICT(note_id, tag_id) DO NOTHING" + "SELECT note_id, tag_id FROM input;" - if _, err := tx.Exec(query, args...); err != nil { + _, err := r.dbContext.ExecContext(context.Background(), query, args) + if err != nil { return err } - return tx.Commit() + return nil } diff --git a/persistence/repositories/uow.go b/persistence/repositories/uow.go new file mode 100644 index 0000000..f2802c8 --- /dev/null +++ b/persistence/repositories/uow.go @@ -0,0 +1,40 @@ +package repositories + +import ( + "context" + "database/sql" +) + +type DBContext interface { + ExecContext(ctx context.Context, args ...any) (sql.Result, error) + QueryContext(ctx context.Context, args ...any) (*sql.Rows, error) + QueryRowContext(ctx context.Context, args ...any) *sql.Row +} + +type UnitOfWork struct { + db *sql.DB +} + +func NewUnitOfWork(db *sql.DB) *UnitOfWork { + return &UnitOfWork{db: db} +} + +func (u *UnitOfWork) Execute(ctx context.Context, fn func(tx *sql.Tx) error) (err error) { + transcation, err := u.db.BeginTx(ctx, nil) + if err != nil { + return err + } + + defer func() { + if err != nil { + _ = transcation.Rollback() + } + }() + + if err = fn(transcation); err != nil { + return err + } + + err = transcation.Commit() + return err +}