feat(uow): added filestore for cross boundary tx

This commit is contained in:
Kristian Borgwarth 2026-04-02 17:56:35 +02:00
parent f070674cdb
commit 884e9cd207
6 changed files with 87 additions and 58 deletions

View file

@ -1,2 +0,0 @@
// Package files provides utilities for working with files and directories.
package files

View file

@ -1,19 +0,0 @@
package files
import "os"
func WriteToFile(path string, data []byte) (filePath string, err error) {
if checkIfFileExists(path) {
return path, nil
}
err = os.WriteFile(path, data, 0o644)
if err != nil {
return "", err
}
return path, nil
}
func checkIfFileExists(path string) bool {
_, err := os.Stat(path)
return err == nil
}

View file

@ -2,11 +2,10 @@ package handlers
import (
"context"
"database/sql"
"encoding/json"
"os"
"github.com/KristianJBorgwarth/dendrite.daemon/core/frontmatter"
"github.com/KristianJBorgwarth/dendrite.daemon/core/template"
"github.com/KristianJBorgwarth/dendrite.daemon/persistence/repositories"
)
@ -17,55 +16,56 @@ type createNoteCommand struct {
Vars map[string]string `json:"vars"`
}
type CreateNoteHandler struct {}
type CreateNoteHandler struct {
uow *repositories.UnitOfWork
}
func NewCreateNoteHandler(uow *repositories.UnitOfWork) *CreateNoteHandler
func NewCreateNoteHandler(uow *repositories.UnitOfWork) *CreateNoteHandler {
return &CreateNoteHandler{uow: uow}
}
func (h CreateNoteHandler) Handle(ctx context.Context, raw json.RawMessage) (any, error) {
func (h *CreateNoteHandler) Handle(ctx context.Context, raw json.RawMessage) (any, error) {
var cmd createNoteCommand
if err := json.Unmarshal(raw, &cmd); err != nil {
return nil, err
}
data, err := h.getTemplate(cmd.TemplatePath)
data, err := template.GenerateTemplate(cmd.TemplatePath)
if err != nil {
return nil, err
}
if data == nil {
data = []byte("---\ntitle: " + cmd.Title + "\ntags: []\n---\n")
}
tags, err := frontmatter.ParseTags(data)
if err != nil {
return nil, err
}
err = h.uow.Execute(ctx, func(tx *sql.Tx) error {
tagRepo := repositories.NewTagRepository(tx)
noteRepo := repositories.NewNoteRepository(tx)
if err = tagRepo.Upsert(ctx, tags); err != nil {
return err
}
if err = noteRepo.Upsert(ctx, cmd.Title, cmd.Path, frontmatter.Slugify(cmd.Title)); err != nil {
return err
}
return nil
})
tx, err := h.uow.Begin()
if err != nil {
return nil, err
}
defer h.uow.Rollback()
tagRepo := repositories.NewTagRepository(tx)
noteRepo := repositories.NewNoteRepository(tx)
if err = tagRepo.Upsert(ctx, tags); err != nil {
return nil, err
}
if err = noteRepo.Upsert(ctx, cmd.Title, cmd.Path, frontmatter.Slugify(cmd.Title)); err != nil {
return nil, err
}
h.uow.FileStore.Stage(cmd.Path, data)
if err = h.uow.Commit(); err != nil {
return nil, err
}
return cmd.Path, nil
}
func (h *CreateNoteHandler) getTemplate(templatePath string) ([]byte, error) {
if templatePath == "" {
return nil, nil
}
data, err := os.ReadFile(templatePath)
if err != nil {
return nil, err
}
return data, nil
}

View file

@ -1,13 +1,15 @@
package repositories
import (
"database/sql"
"github.com/KristianJBorgwarth/dendrite.daemon/persistence/store"
)
type UnitOfWork struct {
db *sql.DB
Transaction *sql.Tx
FileStore *store.FileStore
}
func NewUnitOfWork(db *sql.DB) *UnitOfWork {
@ -30,10 +32,10 @@ func (u *UnitOfWork) Commit() error {
return u.Transaction.Commit()
}
func (u *UnitOfWork) Rollback() error {
func (u *UnitOfWork) Rollback() {
if u.Transaction == nil {
return nil
return
}
return u.Transaction.Rollback()
u.Transaction.Rollback()
u.FileStore.Rollback()
}

2
persistence/store/doc.go Normal file
View file

@ -0,0 +1,2 @@
// Package store provides access to any in memory stores related to files and index
package store

View file

@ -0,0 +1,46 @@
package store
import "os"
type FileStore struct {
staged []stagedFile
commited []string
}
type stagedFile struct {
path string
data []byte
}
func (fs *FileStore) Stage(path string, data []byte) {
fs.staged = append(fs.staged, stagedFile{path: path, data: data})
}
func (fs *FileStore) Flush() error {
for _, file := range fs.staged {
if fs.fileExists(file.path) {
continue
}
if err := os.WriteFile(file.path, file.data, 0o644); err != nil {
return err
}
fs.commited = append(fs.commited, file.path)
}
fs.staged = nil
return nil
}
func (fs *FileStore) Rollback() error {
for _, path := range fs.commited {
if err := os.Remove(path); err != nil {
return err
}
}
fs.commited = nil
return nil
}
func (fs *FileStore) fileExists(path string) bool {
_, err := os.Stat(path)
return err == nil
}