Compare commits
10 commits
38b4cb815f
...
4025f03826
| Author | SHA1 | Date | |
|---|---|---|---|
| 4025f03826 | |||
| a21ff37225 | |||
| 86ac6b33a4 | |||
| 865430d21c | |||
| 2f94782d33 | |||
| 87536b474a | |||
| f1bc87e3ad | |||
| d702e935b5 | |||
| fdb7ceb190 | |||
| 0eeb588983 |
28 changed files with 555 additions and 89 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
dendrite
|
||||||
11
cmd/main.go
11
cmd/main.go
|
|
@ -26,21 +26,24 @@ func main() {
|
||||||
linkRepo := repositories.NewLinkRepository(persistence.NewReadContext())
|
linkRepo := repositories.NewLinkRepository(persistence.NewReadContext())
|
||||||
tagRepo := repositories.NewTagRepository(persistence.NewReadContext())
|
tagRepo := repositories.NewTagRepository(persistence.NewReadContext())
|
||||||
noteRepo := repositories.NewNoteRepository(persistence.NewReadContext())
|
noteRepo := repositories.NewNoteRepository(persistence.NewReadContext())
|
||||||
|
cfeRepo := repositories.NewCfeRepository(persistence.NewReadContext())
|
||||||
|
|
||||||
tagService := services.NewTagService(tagRepo)
|
tagService := services.NewTagService(tagRepo)
|
||||||
linkService := services.NewLinkService(linkRepo)
|
linkService := services.NewLinkService(linkRepo)
|
||||||
noteService := services.NewNoteService(tagRepo, linkRepo, noteRepo)
|
noteService := services.NewNoteService(tagRepo, linkRepo, noteRepo, cfeRepo)
|
||||||
idxr := services.NewIndexRebuilder(uow, noteRepo, linkRepo, tagRepo, indexRepo )
|
cfeSvc := services.NewCfeService(cfeRepo)
|
||||||
|
idxr := services.NewIndexRebuilder(uow, noteRepo, linkRepo, tagRepo, indexRepo, cfeRepo)
|
||||||
|
|
||||||
server.RegisterHandler("vault/init", vault.NewInitializeHandler(idxr, noteService))
|
server.RegisterHandler("vault/init", vault.NewInitializeHandler(idxr, noteService))
|
||||||
server.RegisterHandler("vault/rebuild", vault.NewRebuildIndexHandler(idxr))
|
server.RegisterHandler("vault/rebuild", vault.NewRebuildIndexHandler(idxr))
|
||||||
|
|
||||||
server.RegisterHandler("note/create", note.NewCreateNoteHandler(uow, tagService, noteRepo))
|
server.RegisterHandler("note/create", note.NewCreateNoteHandler(uow, tagService, noteRepo, cfeSvc))
|
||||||
server.RegisterHandler("note/save", note.NewSaveNoteHandler(uow, noteRepo, tagService, noteService, linkService))
|
server.RegisterHandler("note/save", note.NewSaveNoteHandler(uow, noteRepo, tagService, noteService, linkService, cfeSvc))
|
||||||
server.RegisterHandler("note/delete", note.NewDeleteNoteHandler(uow, tagService, noteService))
|
server.RegisterHandler("note/delete", note.NewDeleteNoteHandler(uow, tagService, noteService))
|
||||||
server.RegisterHandler("note/goto", note.NewGotoNoteHandler(noteRepo))
|
server.RegisterHandler("note/goto", note.NewGotoNoteHandler(noteRepo))
|
||||||
server.RegisterHandler("note/backlinks", note.NewGetBackLinksHandler(linkRepo, noteRepo))
|
server.RegisterHandler("note/backlinks", note.NewGetBackLinksHandler(linkRepo, noteRepo))
|
||||||
server.RegisterHandler("note/search_by_tag", note.NewGetNotesByTagHandler(noteRepo))
|
server.RegisterHandler("note/search_by_tag", note.NewGetNotesByTagHandler(noteRepo))
|
||||||
|
server.RegisterHandler("note/search_by_cf", note.NewGetNotesByCfeHandler(cfeRepo))
|
||||||
|
|
||||||
server.RegisterHandler("completion/tag", completion.NewCompleteTagHandler(tagRepo))
|
server.RegisterHandler("completion/tag", completion.NewCompleteTagHandler(tagRepo))
|
||||||
server.RegisterHandler("completion/slug", completion.NewCompleteSlugHandler(noteRepo))
|
server.RegisterHandler("completion/slug", completion.NewCompleteSlugHandler(noteRepo))
|
||||||
|
|
|
||||||
|
|
@ -17,3 +17,11 @@ func NewNoteDto(note *models.Note) *NoteDto {
|
||||||
Slug: note.Slug(),
|
Slug: note.Slug(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewNoteDtos(notes []*models.Note) []*NoteDto {
|
||||||
|
dtos := make([]*NoteDto, len(notes))
|
||||||
|
for i, note := range notes {
|
||||||
|
dtos[i] = NewNoteDto(note)
|
||||||
|
}
|
||||||
|
return dtos
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ type FrontMatter struct {
|
||||||
Created string `yaml:"created"`
|
Created string `yaml:"created"`
|
||||||
Updated string `yaml:"updated"`
|
Updated string `yaml:"updated"`
|
||||||
Date string `yaml:"date"`
|
Date string `yaml:"date"`
|
||||||
Author string `yaml:"author"`
|
Custom map[string]any `yaml:",inline"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseFrontMatter(file []byte) (*FrontMatter, error) {
|
func ParseFrontMatter(file []byte) (*FrontMatter, error) {
|
||||||
|
|
|
||||||
|
|
@ -20,16 +20,18 @@ type createNoteCommand struct {
|
||||||
|
|
||||||
type CreateNoteHandler struct {
|
type CreateNoteHandler struct {
|
||||||
uow *repositories.UnitOfWork
|
uow *repositories.UnitOfWork
|
||||||
tagService services.ITagService
|
tagSvc services.ITagService
|
||||||
noteRepo repositories.INoteRepository
|
noteRepo repositories.INoteRepository
|
||||||
|
cfeSvc services.ICfService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCreateNoteHandler(
|
func NewCreateNoteHandler(
|
||||||
uow *repositories.UnitOfWork,
|
uow *repositories.UnitOfWork,
|
||||||
tagRepo services.ITagService,
|
tagSvc services.ITagService,
|
||||||
noteRepo repositories.INoteRepository,
|
noteRepo repositories.INoteRepository,
|
||||||
|
cfeSvc services.ICfService,
|
||||||
) *CreateNoteHandler {
|
) *CreateNoteHandler {
|
||||||
return &CreateNoteHandler{uow, tagRepo, noteRepo}
|
return &CreateNoteHandler{uow, tagSvc, noteRepo, cfeSvc}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *CreateNoteHandler) Handle(ctx context.Context, raw json.RawMessage) (any, error) {
|
func (h *CreateNoteHandler) Handle(ctx context.Context, raw json.RawMessage) (any, error) {
|
||||||
|
|
@ -62,7 +64,7 @@ func (h *CreateNoteHandler) Handle(ctx context.Context, raw json.RawMessage) (an
|
||||||
return notePath, nil
|
return notePath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
tagModels, err := h.tagService.CreateTags(ctx, dbCtx, template.FrontMatter.Tags)
|
tagModels, err := h.tagSvc.CreateTags(ctx, dbCtx, template.FrontMatter.Tags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -73,7 +75,11 @@ func (h *CreateNoteHandler) Handle(ctx context.Context, raw json.RawMessage) (an
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = h.tagService.CreateNoteTags(ctx, dbCtx, note.ID(), tagModels); err != nil {
|
if err = h.tagSvc.CreateNoteTags(ctx, dbCtx, note.ID(), tagModels); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = h.cfeSvc.AddCfe(ctx, dbCtx, note.ID(), template.FrontMatter.Custom); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
47
core/handlers/note/get_notes_by_cf_handler.go
Normal file
47
core/handlers/note/get_notes_by_cf_handler.go
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
package note
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/KristianJBorgwarth/dendrite.daemon/core/dtos"
|
||||||
|
"github.com/KristianJBorgwarth/dendrite.daemon/persistence/repositories"
|
||||||
|
)
|
||||||
|
|
||||||
|
type getNotesByCfQuery struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetNotesByCfHandler struct {
|
||||||
|
cfeRepo repositories.ICfRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGetNotesByCfeHandler(
|
||||||
|
cfeRepo repositories.ICfRepository,
|
||||||
|
) *GetNotesByCfHandler {
|
||||||
|
return &GetNotesByCfHandler{cfeRepo: cfeRepo}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *GetNotesByCfHandler) Handle(
|
||||||
|
ctx context.Context,
|
||||||
|
raw json.RawMessage,
|
||||||
|
) (any, error) {
|
||||||
|
var query getNotesByCfQuery
|
||||||
|
if err := json.Unmarshal(raw, &query); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
notes, err := h.cfeRepo.Search(ctx, query.Key, query.Value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(notes) == 0 {
|
||||||
|
return make([]*dtos.NoteDto,0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
noteDtos := dtos.NewNoteDtos(notes)
|
||||||
|
|
||||||
|
return noteDtos, nil
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/KristianJBorgwarth/dendrite.daemon/core/dtos"
|
"github.com/KristianJBorgwarth/dendrite.daemon/core/dtos"
|
||||||
"github.com/KristianJBorgwarth/dendrite.daemon/persistence/repositories"
|
"github.com/KristianJBorgwarth/dendrite.daemon/persistence/repositories"
|
||||||
)
|
)
|
||||||
|
|
||||||
type getNotesByTagCommand struct {
|
type getNotesByTagCommand struct {
|
||||||
Tag string `json:"tag"`
|
Tag string `json:"tag"`
|
||||||
}
|
}
|
||||||
|
|
@ -30,10 +31,7 @@ func (h *GetNotesByTagHandler) Handle(ctx context.Context, raw json.RawMessage)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
noteDtos := make([]*dtos.NoteDto, len(notes))
|
noteDtos := dtos.NewNoteDtos(notes)
|
||||||
for i, note := range notes {
|
|
||||||
noteDtos[i] = dtos.NewNoteDto(note)
|
|
||||||
}
|
|
||||||
|
|
||||||
return noteDtos, nil
|
return noteDtos, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ type SaveNoteHandler struct {
|
||||||
tagService services.ITagService
|
tagService services.ITagService
|
||||||
noteService services.INoteService
|
noteService services.INoteService
|
||||||
linkService services.ILinkService
|
linkService services.ILinkService
|
||||||
|
cfeSvc services.ICfService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSaveNoteHandler(
|
func NewSaveNoteHandler(
|
||||||
|
|
@ -28,8 +29,9 @@ func NewSaveNoteHandler(
|
||||||
ts services.ITagService,
|
ts services.ITagService,
|
||||||
ns services.INoteService,
|
ns services.INoteService,
|
||||||
ls services.ILinkService,
|
ls services.ILinkService,
|
||||||
|
cfs services.ICfService,
|
||||||
) *SaveNoteHandler {
|
) *SaveNoteHandler {
|
||||||
return &SaveNoteHandler{uow, nr, ts, ns, ls}
|
return &SaveNoteHandler{uow, nr, ts, ns, ls, cfs}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *SaveNoteHandler) Handle(ctx context.Context, raw json.RawMessage) (any, error) {
|
func (h *SaveNoteHandler) Handle(ctx context.Context, raw json.RawMessage) (any, error) {
|
||||||
|
|
@ -84,6 +86,10 @@ func (h *SaveNoteHandler) Handle(ctx context.Context, raw json.RawMessage) (any,
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = h.cfeSvc.AddCfe(ctx, tx, note.ID(), file.FrontMatter.Custom); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if err := h.uow.Commit(); err != nil {
|
if err := h.uow.Commit(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package vault
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
|
||||||
"github.com/KristianJBorgwarth/dendrite.daemon/core/services"
|
"github.com/KristianJBorgwarth/dendrite.daemon/core/services"
|
||||||
|
|
@ -11,11 +12,22 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type initializeCommand struct {
|
type initializeCommand struct {
|
||||||
VaultName string `json:"vaultName"`
|
Config Config `json:"config"`
|
||||||
VaultPath string `json:"vaultPath"`
|
}
|
||||||
TemplateDirectory string `json:"templateDirectory"`
|
|
||||||
ExcludeIndexFiles []string `json:"excludeIndexFiles"`
|
type Config struct {
|
||||||
OverrideDefaultIgnores bool `json:"overrideDefaultIgnores"`
|
VaultName string `json:"vault_name"`
|
||||||
|
VaultPath string `json:"vault_path"`
|
||||||
|
TemplatesDir string `json:"templates_dir"`
|
||||||
|
ExcludeIndexFiles []string `json:"exclude_index_files"`
|
||||||
|
OverrideDefaultIgnores bool `json:"override_default_ignores"`
|
||||||
|
DailyNotes DailyNotes `json:"daily_notes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DailyNotes struct {
|
||||||
|
Dir string `json:"dir"`
|
||||||
|
FilenameFormat string `json:"filename_format"`
|
||||||
|
TemplateName string `json:"template_name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type InitializeHandler struct {
|
type InitializeHandler struct {
|
||||||
|
|
@ -34,12 +46,20 @@ func (h InitializeHandler) Handle(ctx context.Context, raw json.RawMessage) (any
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cmd.Config.VaultPath == "" {
|
||||||
|
return nil, errors.New("vault_path is empty — check your dendrite.nvim config")
|
||||||
|
}
|
||||||
|
|
||||||
store := store.NewVaultStore(
|
store := store.NewVaultStore(
|
||||||
cmd.VaultName,
|
cmd.Config.VaultName,
|
||||||
cmd.VaultPath,
|
cmd.Config.VaultPath,
|
||||||
cmd.TemplateDirectory,
|
cmd.Config.TemplatesDir,
|
||||||
cmd.ExcludeIndexFiles,
|
cmd.Config.ExcludeIndexFiles,
|
||||||
cmd.OverrideDefaultIgnores)
|
cmd.Config.OverrideDefaultIgnores,
|
||||||
|
cmd.Config.DailyNotes.Dir,
|
||||||
|
cmd.Config.DailyNotes.FilenameFormat,
|
||||||
|
cmd.Config.DailyNotes.TemplateName,
|
||||||
|
)
|
||||||
|
|
||||||
err := persistence.InitializeDBContext(store.Config.VaultName())
|
err := persistence.InitializeDBContext(store.Config.VaultName())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -56,7 +76,7 @@ func (h InitializeHandler) Handle(ctx context.Context, raw json.RawMessage) (any
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err = h.idxRebuilder.RebuildIndex(ctx, cmd.VaultPath)
|
err = h.idxRebuilder.RebuildIndex(ctx, cmd.Config.VaultPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
27
core/models/custom_frontmatter.go
Normal file
27
core/models/custom_frontmatter.go
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
type CustomFronMatter struct {
|
||||||
|
nodeID string
|
||||||
|
key string
|
||||||
|
value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCustomFrontMatter(nodeID, key, value string) *CustomFronMatter {
|
||||||
|
return &CustomFronMatter{
|
||||||
|
nodeID: nodeID,
|
||||||
|
key: key,
|
||||||
|
value: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfm *CustomFronMatter) NodeID() string {
|
||||||
|
return cfm.nodeID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfm *CustomFronMatter) Key() string {
|
||||||
|
return cfm.key
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfm *CustomFronMatter) Value() string {
|
||||||
|
return cfm.value
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import filehandling "github.com/KristianJBorgwarth/dendrite.daemon/core/file_handling"
|
import (
|
||||||
|
"errors"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
filehandling "github.com/KristianJBorgwarth/dendrite.daemon/core/file_handling"
|
||||||
|
)
|
||||||
|
|
||||||
func MapToLinkModel(noteID string, extractedLinks []*filehandling.ExtractedLink) []*Link {
|
func MapToLinkModel(noteID string, extractedLinks []*filehandling.ExtractedLink) []*Link {
|
||||||
var links []*Link
|
var links []*Link
|
||||||
|
|
@ -9,3 +14,24 @@ func MapToLinkModel(noteID string, extractedLinks []*filehandling.ExtractedLink)
|
||||||
}
|
}
|
||||||
return links
|
return links
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MapToCustomFrontmatter(noteID string, extractedCfe map[string]any) ([]*CustomFronMatter, error) {
|
||||||
|
var cfe []*CustomFronMatter
|
||||||
|
for key, value := range extractedCfe {
|
||||||
|
if valueStr, ok := value.(string); ok {
|
||||||
|
cfe = append(cfe, NewCustomFrontMatter(noteID, key, valueStr))
|
||||||
|
continue
|
||||||
|
} else if valueArr, ok := value.([]any); ok {
|
||||||
|
for _, v := range valueArr {
|
||||||
|
if s, ok := v.(string); ok {
|
||||||
|
cfe = append(cfe, NewCustomFrontMatter(noteID, key, s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
slog.Warn("Unsupported CFE value type, skipping", "key", key, "value", value)
|
||||||
|
return nil, errors.New("unsupported CFE value type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cfe, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
37
core/services/cf_service.go
Normal file
37
core/services/cf_service.go
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
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 ICfService interface {
|
||||||
|
AddCfe(ctx context.Context, dbCtx persistence.IDbContext, noteID string, cfe map[string]any) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type cfService struct {
|
||||||
|
cfRepo repositories.ICfRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCfeService(cfRepo repositories.ICfRepository) ICfService {
|
||||||
|
return &cfService{cfRepo}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *cfService) AddCfe(
|
||||||
|
ctx context.Context,
|
||||||
|
dbCtx persistence.IDbContext,
|
||||||
|
noteID string,
|
||||||
|
cfe map[string]any,
|
||||||
|
) error {
|
||||||
|
if len(cfe) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cfModels, err := models.MapToCustomFrontmatter(noteID, cfe)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.cfRepo.InsertRange(ctx, dbCtx, cfModels)
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,14 @@ import (
|
||||||
"github.com/KristianJBorgwarth/dendrite.daemon/persistence/store"
|
"github.com/KristianJBorgwarth/dendrite.daemon/persistence/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type index struct {
|
||||||
|
notes []*models.Note
|
||||||
|
links []*models.Link
|
||||||
|
cfe []*models.CustomFronMatter
|
||||||
|
tags []*models.Tag
|
||||||
|
noteTags []*models.NoteTag
|
||||||
|
}
|
||||||
|
|
||||||
type IIndexRebuilder interface {
|
type IIndexRebuilder interface {
|
||||||
RebuildIndex(ctx context.Context, vaultRoot string) error
|
RebuildIndex(ctx context.Context, vaultRoot string) error
|
||||||
}
|
}
|
||||||
|
|
@ -25,6 +33,7 @@ type indexRebuilder struct {
|
||||||
linkRepo repositories.ILinkRepository
|
linkRepo repositories.ILinkRepository
|
||||||
tagRepo repositories.ITagRepository
|
tagRepo repositories.ITagRepository
|
||||||
indexRepo repositories.IIndexRepository
|
indexRepo repositories.IIndexRepository
|
||||||
|
cfeRepo repositories.ICfRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIndexRebuilder(
|
func NewIndexRebuilder(
|
||||||
|
|
@ -33,6 +42,7 @@ func NewIndexRebuilder(
|
||||||
linkRepo repositories.ILinkRepository,
|
linkRepo repositories.ILinkRepository,
|
||||||
tagRepo repositories.ITagRepository,
|
tagRepo repositories.ITagRepository,
|
||||||
indexRepo repositories.IIndexRepository,
|
indexRepo repositories.IIndexRepository,
|
||||||
|
cfeRepo repositories.ICfRepository,
|
||||||
) *indexRebuilder {
|
) *indexRebuilder {
|
||||||
return &indexRebuilder{
|
return &indexRebuilder{
|
||||||
uow: uow,
|
uow: uow,
|
||||||
|
|
@ -40,6 +50,7 @@ func NewIndexRebuilder(
|
||||||
linkRepo: linkRepo,
|
linkRepo: linkRepo,
|
||||||
tagRepo: tagRepo,
|
tagRepo: tagRepo,
|
||||||
indexRepo: indexRepo,
|
indexRepo: indexRepo,
|
||||||
|
cfeRepo: cfeRepo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,13 +65,16 @@ func (r *indexRebuilder) RebuildIndex(ctx context.Context, vaultRoot string) err
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
notes, links, tags, noteTags := r.buildDBModels(files)
|
index, err := r.buildDBModels(files)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err = r.indexRepo.WipeIndex(ctx, dbctx); err != nil {
|
if err = r.indexRepo.WipeIndex(ctx, dbctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = r.buildIndex(ctx, dbctx, notes, links, tags, noteTags); err != nil {
|
if err = r.buildIndex(ctx, dbctx, index); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,24 +88,25 @@ func (r *indexRebuilder) RebuildIndex(ctx context.Context, vaultRoot string) err
|
||||||
func (r *indexRebuilder) buildIndex(
|
func (r *indexRebuilder) buildIndex(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
dbctx persistence.IDbContext,
|
dbctx persistence.IDbContext,
|
||||||
notes []*models.Note,
|
index *index,
|
||||||
links []*models.Link,
|
|
||||||
tags []*models.Tag,
|
|
||||||
noteTags []*models.NoteTag,
|
|
||||||
) error {
|
) error {
|
||||||
if err := r.noteRepo.InsertRange(ctx, dbctx, notes); err != nil {
|
if err := r.noteRepo.InsertRange(ctx, dbctx, index.notes); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := r.linkRepo.InsertRange(ctx, dbctx, links); err != nil {
|
if err := r.linkRepo.InsertRange(ctx, dbctx, index.links); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := r.tagRepo.InsertRange(ctx, dbctx, tags); err != nil {
|
if err := r.tagRepo.InsertRange(ctx, dbctx, index.tags); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := r.tagRepo.InsertNoteTags(ctx, dbctx, noteTags); err != nil {
|
if err := r.tagRepo.InsertNoteTags(ctx, dbctx, index.noteTags); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := r.cfeRepo.InsertRange(ctx, dbctx, index.cfe); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,9 +161,10 @@ func (r *indexRebuilder) IsValidDirectory(path string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *indexRebuilder) buildDBModels(files []*filehandling.File) ([]*models.Note, []*models.Link, []*models.Tag, []*models.NoteTag) {
|
func (r *indexRebuilder) buildDBModels(files []*filehandling.File) (*index, error) {
|
||||||
var notes []*models.Note
|
var notes []*models.Note
|
||||||
var links []*models.Link
|
var links []*models.Link
|
||||||
|
var cfe []*models.CustomFronMatter
|
||||||
tagMap := make(map[string]*models.Tag)
|
tagMap := make(map[string]*models.Tag)
|
||||||
var noteTags []*models.NoteTag
|
var noteTags []*models.NoteTag
|
||||||
|
|
||||||
|
|
@ -160,6 +176,12 @@ func (r *indexRebuilder) buildDBModels(files []*filehandling.File) ([]*models.No
|
||||||
}
|
}
|
||||||
noteTags = append(noteTags, models.CreateNoteTags(note.ID(), file.FrontMatter.Tags)...)
|
noteTags = append(noteTags, models.CreateNoteTags(note.ID(), file.FrontMatter.Tags)...)
|
||||||
links = append(links, models.MapToLinkModel(note.ID(), file.ExtractedLinks)...)
|
links = append(links, models.MapToLinkModel(note.ID(), file.ExtractedLinks)...)
|
||||||
|
|
||||||
|
mappedCfe, err := models.MapToCustomFrontmatter(note.ID(), file.FrontMatter.Custom)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cfe = append(cfe, mappedCfe...)
|
||||||
}
|
}
|
||||||
|
|
||||||
tags := make([]*models.Tag, 0, len(tagMap))
|
tags := make([]*models.Tag, 0, len(tagMap))
|
||||||
|
|
@ -167,5 +189,11 @@ func (r *indexRebuilder) buildDBModels(files []*filehandling.File) ([]*models.No
|
||||||
tags = append(tags, tag)
|
tags = append(tags, tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
return notes, links, tags, noteTags
|
return &index{
|
||||||
|
notes: notes,
|
||||||
|
links: links,
|
||||||
|
tags: tags,
|
||||||
|
noteTags: noteTags,
|
||||||
|
cfe: cfe,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,14 +21,16 @@ type noteService struct {
|
||||||
tagRepo repositories.ITagRepository
|
tagRepo repositories.ITagRepository
|
||||||
linkRepo repositories.ILinkRepository
|
linkRepo repositories.ILinkRepository
|
||||||
noteRepo repositories.INoteRepository
|
noteRepo repositories.INoteRepository
|
||||||
|
cfeRepo repositories.ICfRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNoteService(
|
func NewNoteService(
|
||||||
tagRepo repositories.ITagRepository,
|
tagRepo repositories.ITagRepository,
|
||||||
linkRepo repositories.ILinkRepository,
|
linkRepo repositories.ILinkRepository,
|
||||||
noteRepo repositories.INoteRepository,
|
noteRepo repositories.INoteRepository,
|
||||||
|
cfeRepo repositories.ICfRepository,
|
||||||
) INoteService {
|
) INoteService {
|
||||||
return ¬eService{tagRepo, linkRepo, noteRepo}
|
return ¬eService{tagRepo, linkRepo, noteRepo, cfeRepo}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *noteService) CreateNote(ctx context.Context, dbCtx persistence.IDbContext, path, title, slug string) (*models.Note, error) {
|
func (s *noteService) CreateNote(ctx context.Context, dbCtx persistence.IDbContext, path, title, slug string) (*models.Note, error) {
|
||||||
|
|
@ -56,6 +58,10 @@ func (s *noteService) DeleteNoteMetaData(ctx context.Context, dbCtx persistence.
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := s.cfeRepo.Delete(ctx, dbCtx, noteID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
BIN
dendrite
BIN
dendrite
Binary file not shown.
|
|
@ -49,8 +49,10 @@ ASSET="${BINARY}-${GOOS}-${GOARCH}"
|
||||||
URL="https://github.com/$REPO/releases/download/$TAG/$ASSET"
|
URL="https://github.com/$REPO/releases/download/$TAG/$ASSET"
|
||||||
|
|
||||||
echo "Downloading $ASSET ($TAG)..."
|
echo "Downloading $ASSET ($TAG)..."
|
||||||
curl -fsSL "$URL" -o "$INSTALL_DIR/$BINARY"
|
TMP="$(mktemp)"
|
||||||
chmod +x "$INSTALL_DIR/$BINARY"
|
curl -fsSL "$URL" -o "$TMP"
|
||||||
|
chmod +x "$TMP"
|
||||||
|
mv "$TMP" "$INSTALL_DIR/$BINARY"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "dendrite $TAG installed to $INSTALL_DIR/$BINARY"
|
echo "dendrite $TAG installed to $INSTALL_DIR/$BINARY"
|
||||||
|
|
|
||||||
3
makefile
3
makefile
|
|
@ -7,3 +7,6 @@ test-unit:
|
||||||
|
|
||||||
test-integration:
|
test-integration:
|
||||||
go test ./test/test_integration/...
|
go test ./test/test_integration/...
|
||||||
|
|
||||||
|
build:
|
||||||
|
go build -o dendrite ./cmd
|
||||||
|
|
|
||||||
|
|
@ -1,50 +1,61 @@
|
||||||
package persistenceconfig
|
package persistenceconfig
|
||||||
|
|
||||||
type VaultConfiguration struct {
|
type DailyNotes struct {
|
||||||
name string
|
dir string
|
||||||
path string
|
filenameFormat string
|
||||||
templateDirectory string
|
templateName string
|
||||||
exludeIndexFiles []string
|
}
|
||||||
defaultExcludes []string
|
|
||||||
|
func NewDailyNotes(dir, filenameFormat, templateName string) DailyNotes {
|
||||||
|
return DailyNotes{
|
||||||
|
dir: dir,
|
||||||
|
filenameFormat: filenameFormat,
|
||||||
|
templateName: templateName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type VaultConfig struct {
|
||||||
|
vaultName string
|
||||||
|
vaultPath string
|
||||||
|
templatesDir string
|
||||||
|
excludeIndexFiles []string
|
||||||
overrideDefaultIgnores bool
|
overrideDefaultIgnores bool
|
||||||
|
dailyNotes DailyNotes
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewVaultConfiguration(
|
func NewVaultConfiguration(
|
||||||
vaultName,
|
vaultName,
|
||||||
vaultPath,
|
vaultPath,
|
||||||
templateDirectory string,
|
templatesDir string,
|
||||||
excludeIndexFiles []string,
|
excludeIndexFiles []string,
|
||||||
overrideDefaultIgnores bool,
|
overrideDefaultIgnores bool,
|
||||||
) *VaultConfiguration {
|
dailyNotes DailyNotes,
|
||||||
return &VaultConfiguration{
|
) *VaultConfig {
|
||||||
name: vaultName,
|
return &VaultConfig{
|
||||||
path: vaultPath,
|
vaultName: vaultName,
|
||||||
templateDirectory: templateDirectory,
|
vaultPath: vaultPath,
|
||||||
exludeIndexFiles: excludeIndexFiles,
|
templatesDir: templatesDir,
|
||||||
defaultExcludes: []string{".git", ".templates"},
|
dailyNotes: dailyNotes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *VaultConfiguration) VaultName() string {
|
func (vc *VaultConfig) VaultName() string {
|
||||||
return vc.name
|
return vc.vaultName
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *VaultConfiguration) VaultPath() string {
|
func (vc *VaultConfig) VaultPath() string {
|
||||||
return vc.path
|
return vc.vaultPath
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *VaultConfiguration) TemplateDirectory() string {
|
func (vc *VaultConfig) TemplateDirectory() string {
|
||||||
return vc.templateDirectory
|
return vc.templatesDir
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *VaultConfiguration) ExcludeIndexFiles() []string {
|
func (vc *VaultConfig) ExcludeIndexFiles() []string {
|
||||||
return vc.exludeIndexFiles
|
defaultIgnores := []string{"index.md", "index", ".templates"}
|
||||||
}
|
|
||||||
|
|
||||||
func (vc *VaultConfiguration) GetExcludeIndexFiles() []string {
|
|
||||||
defaultIgnores := []string{".git", ".templates"}
|
|
||||||
if vc.overrideDefaultIgnores {
|
if vc.overrideDefaultIgnores {
|
||||||
return vc.exludeIndexFiles
|
return vc.excludeIndexFiles
|
||||||
}
|
}
|
||||||
return append(defaultIgnores, vc.exludeIndexFiles...)
|
return append(defaultIgnores, vc.excludeIndexFiles...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
9
persistence/migrations/004_custom_frontmatter.sql
Normal file
9
persistence/migrations/004_custom_frontmatter.sql
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
CREATE TABLE custom_frontmatter(
|
||||||
|
note_id TEXT,
|
||||||
|
key TEXT,
|
||||||
|
value TEXT,
|
||||||
|
PRIMARY KEY (note_id, key, value),
|
||||||
|
FOREIGN KEY (note_id) REFERENCES note(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_cfe_key_value on custom_frontmatter(key, value);
|
||||||
91
persistence/repositories/cfe_repository.go
Normal file
91
persistence/repositories/cfe_repository.go
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
package repositories
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/KristianJBorgwarth/dendrite.daemon/core/models"
|
||||||
|
"github.com/KristianJBorgwarth/dendrite.daemon/persistence"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ICfRepository interface {
|
||||||
|
InsertRange(ctx context.Context, dbContext persistence.IDbContext, cfe []*models.CustomFronMatter) error
|
||||||
|
Search(ctx context.Context, key string, value string) ([]*models.Note, error)
|
||||||
|
Delete(ctx context.Context, dbContext persistence.IDbContext, noteID string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type cfeRepository struct {
|
||||||
|
readDBContext persistence.ReadContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCfeRepository(rdb persistence.ReadContext) ICfRepository {
|
||||||
|
return &cfeRepository{readDBContext: rdb}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *cfeRepository) InsertRange(
|
||||||
|
ctx context.Context,
|
||||||
|
dbCtx persistence.IDbContext,
|
||||||
|
cfe []*models.CustomFronMatter,
|
||||||
|
) error {
|
||||||
|
statement, err := dbCtx.Prepare(
|
||||||
|
`INSERT INTO custom_frontmatter (note_id, key, value)
|
||||||
|
VALUES (?, ?, ?) ON CONFLICT DO NOTHING;`)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cfe {
|
||||||
|
if _, err := statement.ExecContext(ctx, c.NodeID(), c.Key(), c.Value()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *cfeRepository) Delete(
|
||||||
|
ctx context.Context,
|
||||||
|
dbCtx persistence.IDbContext,
|
||||||
|
noteID string,
|
||||||
|
) error {
|
||||||
|
statement, err := dbCtx.Prepare(`DELETE FROM custom_frontmatter WHERE note_id = ?;`)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := statement.ExecContext(ctx, noteID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *cfeRepository) Search(
|
||||||
|
ctx context.Context,
|
||||||
|
key string,
|
||||||
|
value string,
|
||||||
|
) ([]*models.Note, error) {
|
||||||
|
rows, err := r.readDBContext.QueryContext(
|
||||||
|
ctx,
|
||||||
|
`SELECT n.id, n.path, n.title, n.slug, n.created_at, n.updated_at
|
||||||
|
FROM note n
|
||||||
|
INNER JOIN custom_frontmatter cfe ON n.id = cfe.note_id
|
||||||
|
WHERE cfe.key = ? AND cfe.value LIKE ?;`,
|
||||||
|
key,
|
||||||
|
"%"+value+"%",
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var notes []*models.Note
|
||||||
|
for rows.Next() {
|
||||||
|
var id, title, path, slug, createdAt, updatedAT string
|
||||||
|
if err := rows.Scan(&id, &title, &path, &slug, &createdAt, &updatedAT); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
notes = append(notes, models.NewNote(id, title, path, slug, createdAt, updatedAT))
|
||||||
|
}
|
||||||
|
|
||||||
|
return notes, nil
|
||||||
|
}
|
||||||
|
|
@ -22,7 +22,8 @@ func (r *indexRepository) WipeIndex(ctx context.Context, dbContext persistence.I
|
||||||
cmd := `DELETE FROM note;
|
cmd := `DELETE FROM note;
|
||||||
DELETE FROM tag;
|
DELETE FROM tag;
|
||||||
DELETE FROM note_tag;
|
DELETE FROM note_tag;
|
||||||
DELETE FROM link;`
|
DELETE FROM link;
|
||||||
|
DELETE FROM custom_frontmatter;`
|
||||||
|
|
||||||
_, err := dbContext.ExecContext(ctx, cmd)
|
_, err := dbContext.ExecContext(ctx, cmd)
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type VaultStore struct {
|
type VaultStore struct {
|
||||||
Config *persistenceconfig.VaultConfiguration
|
Config *persistenceconfig.VaultConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
var vaultStore *VaultStore
|
var vaultStore *VaultStore
|
||||||
|
|
@ -18,6 +18,9 @@ func NewVaultStore(
|
||||||
templateDir string,
|
templateDir string,
|
||||||
exludeIndexFiles []string,
|
exludeIndexFiles []string,
|
||||||
overrideDefaultIgnores bool,
|
overrideDefaultIgnores bool,
|
||||||
|
dailyDir string,
|
||||||
|
dailyFilenameFormat string,
|
||||||
|
dailyTemplateName string,
|
||||||
) *VaultStore {
|
) *VaultStore {
|
||||||
if vaultStore != nil {
|
if vaultStore != nil {
|
||||||
return vaultStore
|
return vaultStore
|
||||||
|
|
@ -28,7 +31,7 @@ func NewVaultStore(
|
||||||
templateDir,
|
templateDir,
|
||||||
exludeIndexFiles,
|
exludeIndexFiles,
|
||||||
overrideDefaultIgnores,
|
overrideDefaultIgnores,
|
||||||
)}
|
persistenceconfig.NewDailyNotes(dailyDir, dailyFilenameFormat, dailyTemplateName))}
|
||||||
return vaultStore
|
return vaultStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,13 +42,10 @@ func GetVaultStore() *VaultStore {
|
||||||
return vaultStore
|
return vaultStore
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vs *VaultStore) SetConfig(config persistenceconfig.VaultConfiguration) {
|
|
||||||
vs.Config = &config
|
|
||||||
}
|
|
||||||
|
|
||||||
func (vs *VaultStore) GetTemplatePath(templateName string) string {
|
func (vs *VaultStore) GetTemplatePath(templateName string) string {
|
||||||
templateName = vs.fileTypeCheck(templateName)
|
templateName = vs.fileTypeCheck(templateName)
|
||||||
return path.Join(vs.Config.TemplateDirectory(), templateName)
|
path := path.Join(vs.Config.VaultPath(), vs.Config.TemplateDirectory(), templateName)
|
||||||
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vs *VaultStore) fileTypeCheck(templateName string) string {
|
func (vs *VaultStore) fileTypeCheck(templateName string) string {
|
||||||
|
|
@ -57,5 +57,5 @@ func (vs *VaultStore) fileTypeCheck(templateName string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vs *VaultStore) GetExcludeIndexFiles() []string {
|
func (vs *VaultStore) GetExcludeIndexFiles() []string {
|
||||||
return vs.Config.GetExcludeIndexFiles()
|
return vs.Config.ExcludeIndexFiles()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ func newCreateNoteHandler() *note.CreateNoteHandler {
|
||||||
repositories.NewUnitOfWork(),
|
repositories.NewUnitOfWork(),
|
||||||
services.NewTagService(repositories.NewTagRepository(persistence.NewReadContext())),
|
services.NewTagService(repositories.NewTagRepository(persistence.NewReadContext())),
|
||||||
repositories.NewNoteRepository(persistence.NewReadContext()),
|
repositories.NewNoteRepository(persistence.NewReadContext()),
|
||||||
|
services.NewCfeService(repositories.NewCfeRepository(persistence.NewReadContext())),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,7 +55,7 @@ func TestCreateNoteHandler_WithTemplate_CreatesNoteFileWithTagsAndReturnsPath(t
|
||||||
handler := newCreateNoteHandler()
|
handler := newCreateNoteHandler()
|
||||||
|
|
||||||
vaultPath := Fixture.VaultStore.Config.VaultPath()
|
vaultPath := Fixture.VaultStore.Config.VaultPath()
|
||||||
templateDir := Fixture.VaultStore.Config.TemplateDirectory()
|
templateDir := filepath.Join(Fixture.VaultStore.Config.VaultPath(), Fixture.VaultStore.Config.TemplateDirectory())
|
||||||
require.NoError(t, os.MkdirAll(templateDir, 0o755))
|
require.NoError(t, os.MkdirAll(templateDir, 0o755))
|
||||||
|
|
||||||
templatePath := filepath.Join(templateDir, "template.md")
|
templatePath := filepath.Join(templateDir, "template.md")
|
||||||
|
|
|
||||||
74
test/test_integration/get_notes_by_cfe_handler_test.go
Normal file
74
test/test_integration/get_notes_by_cfe_handler_test.go
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
package integration_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/KristianJBorgwarth/dendrite.daemon/core/dtos"
|
||||||
|
"github.com/KristianJBorgwarth/dendrite.daemon/core/handlers/note"
|
||||||
|
"github.com/KristianJBorgwarth/dendrite.daemon/persistence"
|
||||||
|
"github.com/KristianJBorgwarth/dendrite.daemon/persistence/repositories"
|
||||||
|
"github.com/KristianJBorgwarth/dendrite.daemon/test/test_integration/utils"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newGetNotesByCfeHandler() *note.GetNotesByCfHandler {
|
||||||
|
return note.NewGetNotesByCfeHandler(
|
||||||
|
repositories.NewCfeRepository(persistence.NewReadContext()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Handle_ReturnsNotesWithMacthingCfe(t *testing.T) {
|
||||||
|
// Arrange
|
||||||
|
handler := newGetNotesByCfeHandler()
|
||||||
|
noteID := "test-note-id"
|
||||||
|
cfeKey := "author"
|
||||||
|
cfeValues := []string{"John Doe", "Jane Smith"}
|
||||||
|
utils.CreateNote(Fixture.TestContext, Fixture.DB, noteID, "Test Note", "test-note.md", "test");
|
||||||
|
|
||||||
|
utils.CreateCfe(Fixture.TestContext, Fixture.DB, noteID,
|
||||||
|
cfeKey, cfeValues)
|
||||||
|
|
||||||
|
params, _ := json.Marshal(map[string]any{
|
||||||
|
"key": "author",
|
||||||
|
"value": "J",
|
||||||
|
})
|
||||||
|
|
||||||
|
// Act
|
||||||
|
result, err := handler.Handle(Fixture.TestContext, params)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, result)
|
||||||
|
|
||||||
|
noteDtos, ok := result.([]*dtos.NoteDto)
|
||||||
|
require.True(t, ok)
|
||||||
|
assert.Len(t, noteDtos, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Handle_ReturnsEmptyListWhenNoMatchingCfe(t *testing.T) {
|
||||||
|
// Arrange
|
||||||
|
handler := newGetNotesByCfeHandler()
|
||||||
|
noteID := "test-note-id-2"
|
||||||
|
cfeKey := "category"
|
||||||
|
cfeValues := []string{"Tech", "Lifestyle"}
|
||||||
|
utils.CreateNote(Fixture.TestContext, Fixture.DB, noteID, "Another Test Note", "another-test-note.md", "test");
|
||||||
|
|
||||||
|
utils.CreateCfe(Fixture.TestContext, Fixture.DB, noteID,
|
||||||
|
cfeKey, cfeValues)
|
||||||
|
|
||||||
|
params, _ := json.Marshal(map[string]any{
|
||||||
|
"key": "category",
|
||||||
|
"value": "NonExistingCategory",
|
||||||
|
})
|
||||||
|
|
||||||
|
// Act
|
||||||
|
result, err := handler.Handle(Fixture.TestContext, params)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, result)
|
||||||
|
|
||||||
|
noteDtos, ok := result.([]*dtos.NoteDto)
|
||||||
|
require.True(t, ok)
|
||||||
|
assert.Len(t, noteDtos, 0)
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -46,7 +45,7 @@ func NewDBFixture() *DBFixture {
|
||||||
DB: dbContext.DB,
|
DB: dbContext.DB,
|
||||||
DBPath: dbPath,
|
DBPath: dbPath,
|
||||||
TestContext: context.Background(),
|
TestContext: context.Background(),
|
||||||
VaultStore: store.NewVaultStore("testVault", vaultPath, path.Join(vaultPath, "templates"), []string{}, false),
|
VaultStore: store.NewVaultStore("testVault", vaultPath, "templates", []string{}, false, "", "", ""),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,9 +53,7 @@ var Fixture = NewDBFixture()
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
code := m.Run()
|
code := m.Run()
|
||||||
|
|
||||||
os.RemoveAll(Fixture.DBPath)
|
os.RemoveAll(Fixture.DBPath)
|
||||||
|
|
||||||
os.Exit(code)
|
os.Exit(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
32
test/test_integration/utils/DB_cfe_utils.go
Normal file
32
test/test_integration/utils/DB_cfe_utils.go
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/KristianJBorgwarth/dendrite.daemon/core/models"
|
||||||
|
"github.com/KristianJBorgwarth/dendrite.daemon/persistence"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateCfe(
|
||||||
|
ctx context.Context,
|
||||||
|
dbCtx persistence.IDbContext,
|
||||||
|
noteID, key string,
|
||||||
|
values []string,
|
||||||
|
) error {
|
||||||
|
for _, value := range values {
|
||||||
|
cfe := models.NewCustomFrontMatter(noteID, key, value)
|
||||||
|
statement, err := dbCtx.Prepare(
|
||||||
|
`INSERT INTO custom_frontmatter (note_id, key, value)
|
||||||
|
VALUES (?, ?, ?) ON CONFLICT DO NOTHING;`)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := statement.ExecContext(ctx, cfe.NodeID(), cfe.Key(), cfe.Value()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
30
test/test_integration/utils/DB_note_utils.go
Normal file
30
test/test_integration/utils/DB_note_utils.go
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/KristianJBorgwarth/dendrite.daemon/persistence"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateNote(
|
||||||
|
ctx context.Context,
|
||||||
|
dbCtx persistence.IDbContext,
|
||||||
|
noteID,
|
||||||
|
path,
|
||||||
|
title,
|
||||||
|
slug string,
|
||||||
|
) error {
|
||||||
|
statement, err := dbCtx.Prepare(`
|
||||||
|
INSERT INTO note (id, title, path, slug, created_at, updated_at)
|
||||||
|
VALUES (?, ?, ?, ?, datetime('now'), datetime('now'))
|
||||||
|
ON CONFLICT DO NOTHING;`)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := statement.ExecContext(ctx, noteID, title, path, slug); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
2
test/test_integration/utils/doc.go
Normal file
2
test/test_integration/utils/doc.go
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
// Package utils provides testing utilities for integration tests
|
||||||
|
package utils
|
||||||
Loading…
Add table
Reference in a new issue