Merge pull request #22 from KristianJBorgwarth/feat/save-note-tag-handling

feat: reparse or create note on save
This commit is contained in:
Kristian 2026-04-12 21:18:55 +02:00 committed by GitHub
commit 122b0a271b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 128 additions and 92 deletions

View file

@ -23,10 +23,12 @@ func main() {
tagRepo := repositories.NewTagRepository() tagRepo := repositories.NewTagRepository()
noteRepo := repositories.NewNoteRepository() noteRepo := repositories.NewNoteRepository()
tagService := services.NewTagService(tagRepo) tagService := services.NewTagService(tagRepo)
linkService := services.NewLinkService(linkRepo)
noteService := services.NewNoteService(tagRepo, linkRepo, noteRepo)
server.RegisterHandler("vault/init", vault.NewInitializeHandler()) server.RegisterHandler("vault/init", vault.NewInitializeHandler())
server.RegisterHandler("note/create", note.NewCreateNoteHandler(uow, tagService, noteRepo)) server.RegisterHandler("note/create", note.NewCreateNoteHandler(uow, tagService, noteRepo))
server.RegisterHandler("note/save", note.NewSaveNoteHandler(uow, linkRepo, tagRepo, noteRepo)) server.RegisterHandler("note/save", note.NewSaveNoteHandler(uow, noteRepo, tagService, noteService, linkService))
server.RegisterHandler("completion/link", completion.NewCompleteLinkHandler()) server.RegisterHandler("completion/link", completion.NewCompleteLinkHandler())
if err := server.Run(os.Stdin, os.Stdout); err != nil { if err := server.Run(os.Stdin, os.Stdout); err != nil {

View file

@ -21,13 +21,13 @@ type createNoteCommand struct {
type CreateNoteHandler struct { type CreateNoteHandler struct {
uow *repositories.UnitOfWork uow *repositories.UnitOfWork
tagService services.ITagService tagService services.ITagService
noteRepo repositories.NoteRepository noteRepo repositories.INoteRepository
} }
func NewCreateNoteHandler( func NewCreateNoteHandler(
uow *repositories.UnitOfWork, uow *repositories.UnitOfWork,
tagRepo services.ITagService, tagRepo services.ITagService,
noteRepo repositories.NoteRepository, noteRepo repositories.INoteRepository,
) *CreateNoteHandler { ) *CreateNoteHandler {
return &CreateNoteHandler{uow, tagRepo, noteRepo} return &CreateNoteHandler{uow, tagRepo, noteRepo}
} }

View file

@ -6,8 +6,7 @@ import (
"log/slog" "log/slog"
filehandling "github.com/KristianJBorgwarth/dendrite.daemon/core/file_handling" filehandling "github.com/KristianJBorgwarth/dendrite.daemon/core/file_handling"
"github.com/KristianJBorgwarth/dendrite.daemon/core/models" "github.com/KristianJBorgwarth/dendrite.daemon/core/services"
"github.com/KristianJBorgwarth/dendrite.daemon/persistence"
"github.com/KristianJBorgwarth/dendrite.daemon/persistence/repositories" "github.com/KristianJBorgwarth/dendrite.daemon/persistence/repositories"
) )
@ -16,19 +15,21 @@ type saveNoteCommand struct {
} }
type SaveNoteHandler struct { type SaveNoteHandler struct {
uow *repositories.UnitOfWork uow *repositories.UnitOfWork
linkRepo repositories.ILinkRepository noteRepo repositories.INoteRepository
tagRepo repositories.ITagRepository tagService services.ITagService
noteRepo repositories.NoteRepository noteService services.INoteService
linkService services.ILinkService
} }
func NewSaveNoteHandler( func NewSaveNoteHandler(
uow *repositories.UnitOfWork, uow *repositories.UnitOfWork,
linkRepo repositories.ILinkRepository, nr repositories.INoteRepository,
tagRepo repositories.ITagRepository, ts services.ITagService,
noteRepo repositories.NoteRepository, ns services.INoteService,
ls services.ILinkService,
) *SaveNoteHandler { ) *SaveNoteHandler {
return &SaveNoteHandler{uow, linkRepo, tagRepo, noteRepo} return &SaveNoteHandler{uow, nr, ts, ns, ls}
} }
func (h *SaveNoteHandler) Handle(ctx context.Context, raw json.RawMessage) (any, error) { func (h *SaveNoteHandler) Handle(ctx context.Context, raw json.RawMessage) (any, error) {
@ -49,7 +50,6 @@ func (h *SaveNoteHandler) Handle(ctx context.Context, raw json.RawMessage) (any,
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer h.uow.Rollback() defer h.uow.Rollback()
note, err := h.noteRepo.GetBySlug(ctx, tx, file.Slug) note, err := h.noteRepo.GetBySlug(ctx, tx, file.Slug)
@ -59,90 +59,36 @@ func (h *SaveNoteHandler) Handle(ctx context.Context, raw json.RawMessage) (any,
} }
if note == nil { if note == nil {
if err := h.handleNewNote(ctx, tx, file); err != nil { note, err = h.noteService.CreateNote(ctx, tx, file.Path, file.Title, file.Slug)
if err != nil {
slog.Debug("Failed to save new note", "slug", file.Slug, "error", err)
return nil, err return nil, err
} }
} else { } else {
if err := h.handleExistingNote(ctx, tx, note, file); err != nil { if err = h.noteService.DeleteNoteMetaData(ctx, tx, note.ID()); err != nil {
slog.Debug("Failed to delete existing note metadata", "noteID", note.ID(), "error", err)
return nil, err return nil, err
} }
} }
tagModels, err := h.tagService.CreateTags(ctx, tx, file.FrontMatter.Tags)
if err != nil {
slog.Debug("Failed to create tags", "tags", file.FrontMatter.Tags, "error", err)
return nil, err
}
if err = h.tagService.CreateNoteTags(ctx, tx, note.ID(), tagModels); err != nil {
slog.Debug("Failed to create note tags", "noteID", note.ID(), "tags", file.FrontMatter.Tags, "error", err)
return nil, err
}
if err = h.linkService.CreateLinks(ctx, tx, note.ID(), file.ExtractedLinks); err != nil {
slog.Debug("Failed to create links", "noteID", note.ID(), "links", file.ExtractedLinks, "error", err)
return nil, err
}
if err := h.uow.Commit(); err != nil { if err := h.uow.Commit(); err != nil {
return nil, err return nil, err
} }
return nil, nil return nil, nil
} }
func (h *SaveNoteHandler) handleNewNote(
ctx context.Context,
dbCtx persistence.IDbContext,
file *filehandling.File,
) error {
note := models.CreateNote(file.Path, file.Title, file.Slug)
slog.Debug("EXTRACTED FILE", "path", file.Path, "title", file.Title, "slug", file.Slug, "links", file.ExtractedLinks, "tags", file.FrontMatter.Tags)
if err := h.noteRepo.Insert(ctx, dbCtx, note); err != nil {
slog.Debug("Failed to insert new note", "noteID", note.ID(), "error", err)
return err
}
links := models.MapToLinkModel(note.ID(), file.ExtractedLinks)
if err := h.linkRepo.Insert(ctx, dbCtx, links); err != nil {
slog.Debug("Failed to insert links for new note", "noteID", note.ID(), "error", err)
return err
}
if err := h.tagRepo.InsertNoteTags(ctx, dbCtx, note.ID(), file.FrontMatter.Tags); err != nil {
slog.Debug("Failed to insert tags for new note", "noteID", note.ID(), "error", err)
return err
}
return nil
}
func (h *SaveNoteHandler) handleExistingNote(
ctx context.Context,
dbCtx persistence.IDbContext,
note *models.Note,
file *filehandling.File,
) error {
err := h.deleteExistingNoteRelations(ctx, dbCtx, note.ID())
if err != nil {
slog.Debug("Failed to delete existing note relations", "noteID", note.ID(), "error", err)
return err
}
links := models.MapToLinkModel(note.ID(), file.ExtractedLinks)
if err = h.linkRepo.Insert(ctx, dbCtx, links); err != nil {
slog.Debug("Failed to insert links for existing note", "noteID", note.ID(), "error", err)
return err
}
if err = h.tagRepo.InsertNoteTags(ctx, dbCtx, note.ID(), file.FrontMatter.Tags); err != nil {
slog.Debug("Failed to insert tags for existing note", "noteID", note.ID(), "error", err)
return err
}
return nil
}
func (h *SaveNoteHandler) deleteExistingNoteRelations(
ctx context.Context,
dbCtx persistence.IDbContext,
noteID string,
) error {
if err := h.linkRepo.Delete(ctx, dbCtx, noteID); err != nil {
return err
}
if err := h.tagRepo.DeleteNoteTags(ctx, dbCtx, noteID); err != nil {
return err
}
return nil
}
func (h *SaveNoteHandler) handleTags(ctx context.Context, tagRepo repositories.ITagRepository, noteID string, tags []string) error {
panic("not implemented")
}

View file

@ -0,0 +1,34 @@
package services
import (
"context"
"log/slog"
filehandling "github.com/KristianJBorgwarth/dendrite.daemon/core/file_handling"
"github.com/KristianJBorgwarth/dendrite.daemon/core/models"
"github.com/KristianJBorgwarth/dendrite.daemon/persistence"
"github.com/KristianJBorgwarth/dendrite.daemon/persistence/repositories"
)
type ILinkService interface {
CreateLinks(ctx context.Context, dbCtx persistence.IDbContext, noteID string, links []*filehandling.ExtractedLink) error
}
type linkService struct {
linkRepo repositories.ILinkRepository
}
func NewLinkService(linkRepo repositories.ILinkRepository) ILinkService {
return &linkService{linkRepo}
}
func (s *linkService) CreateLinks(ctx context.Context, dbCtx persistence.IDbContext, noteID string, links []*filehandling.ExtractedLink) error {
if len(links) == 0 {
return nil
}
linkModels := models.MapToLinkModel(noteID, links)
slog.Debug("Creating links", "noteID", noteID, "links", linkModels)
return s.linkRepo.Insert(ctx, dbCtx, linkModels)
}

View file

@ -0,0 +1,49 @@
package services
import (
"context"
"github.com/KristianJBorgwarth/dendrite.daemon/core/models"
"github.com/KristianJBorgwarth/dendrite.daemon/persistence"
"github.com/KristianJBorgwarth/dendrite.daemon/persistence/repositories"
)
type INoteService interface {
CreateNote(ctx context.Context, dbCtx persistence.IDbContext, path, title, slug string) (*models.Note, error)
DeleteNoteMetaData(ctx context.Context, dbCtx persistence.IDbContext, noteID string) error
}
type noteService struct {
tagRepo repositories.ITagRepository
linkRepo repositories.ILinkRepository
noteRepo repositories.INoteRepository
}
func NewNoteService(
tagRepo repositories.ITagRepository,
linkRepo repositories.ILinkRepository,
noteRepo repositories.INoteRepository,
) INoteService {
return &noteService{tagRepo, linkRepo, noteRepo}
}
func (s *noteService) CreateNote(ctx context.Context, dbCtx persistence.IDbContext, path, title, slug string) (*models.Note, error) {
note:= models.CreateNote(path, title, slug)
if err := s.noteRepo.Insert(ctx, dbCtx, note); err != nil {
return nil, err
}
return note, nil
}
func (s *noteService) DeleteNoteMetaData(ctx context.Context, dbCtx persistence.IDbContext, noteID string) error {
if err := s.linkRepo.Delete(ctx, dbCtx, noteID); err != nil {
return err
}
if err := s.tagRepo.DeleteNoteTags(ctx, dbCtx, noteID); err != nil {
return err
}
return nil
}

View file

@ -2,6 +2,7 @@ package services
import ( import (
"context" "context"
"log/slog"
"github.com/KristianJBorgwarth/dendrite.daemon/core/models" "github.com/KristianJBorgwarth/dendrite.daemon/core/models"
"github.com/KristianJBorgwarth/dendrite.daemon/core/utils" "github.com/KristianJBorgwarth/dendrite.daemon/core/utils"
@ -18,7 +19,7 @@ type tagService struct {
tagRepo repositories.ITagRepository tagRepo repositories.ITagRepository
} }
func NewTagService(tagRepo repositories.ITagRepository) *tagService { func NewTagService(tagRepo repositories.ITagRepository) ITagService {
return &tagService{tagRepo} return &tagService{tagRepo}
} }
@ -28,6 +29,8 @@ func (s *tagService) CreateTags(ctx context.Context, dbCtx persistence.IDbContex
return nil, err return nil, err
} }
slog.Debug("Existing tags", "names", names, "existingTags", existingTags)
newTags := utils.Filter(names, func(name string) bool { newTags := utils.Filter(names, func(name string) bool {
for _, tag := range existingTags { for _, tag := range existingTags {
if tag.Name() == name { if tag.Name() == name {
@ -43,6 +46,8 @@ func (s *tagService) CreateTags(ctx context.Context, dbCtx persistence.IDbContex
return nil, err return nil, err
} }
slog.Debug("Created new tags", "newTags", newTagModels)
tags := append(existingTags, newTagModels...) tags := append(existingTags, newTagModels...)
return tags, nil return tags, nil

BIN
dendrite

Binary file not shown.

View file

@ -9,14 +9,14 @@ import (
"github.com/KristianJBorgwarth/dendrite.daemon/persistence" "github.com/KristianJBorgwarth/dendrite.daemon/persistence"
) )
type NoteRepository interface { type INoteRepository interface {
Insert(ctx context.Context, dbContext persistence.IDbContext, note *models.Note) error Insert(ctx context.Context, dbContext persistence.IDbContext, note *models.Note) error
GetBySlug(ctx context.Context, dbContext persistence.IDbContext, slug string) (*models.Note, error) GetBySlug(ctx context.Context, dbContext persistence.IDbContext, slug string) (*models.Note, error)
} }
type noteRepository struct{} type noteRepository struct{}
func NewNoteRepository() NoteRepository { func NewNoteRepository() INoteRepository {
return &noteRepository{} return &noteRepository{}
} }