feat(cfe): get notes by cfe (#46)
This commit is contained in:
parent
2f94782d33
commit
865430d21c
10 changed files with 228 additions and 6 deletions
|
|
@ -43,6 +43,7 @@ func main() {
|
||||||
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_cfe", 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
|
||||||
|
}
|
||||||
|
|
|
||||||
47
core/handlers/note/get_notes_by_cfe_handler.go
Normal file
47
core/handlers/note/get_notes_by_cfe_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 getNotesByCfeQuery struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetNotesByCfeHandler struct {
|
||||||
|
cfeRepo repositories.ICfeRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGetNotesByCfeHandler(
|
||||||
|
cfeRepo repositories.ICfeRepository,
|
||||||
|
) *GetNotesByCfeHandler {
|
||||||
|
return &GetNotesByCfeHandler{cfeRepo: cfeRepo}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *GetNotesByCfeHandler) Handle(
|
||||||
|
ctx context.Context,
|
||||||
|
raw json.RawMessage,
|
||||||
|
) (any, error) {
|
||||||
|
var query getNotesByCfeQuery
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
type ICfeRepository interface {
|
type ICfeRepository interface {
|
||||||
InsertRange(ctx context.Context, dbContext persistence.IDbContext, cfe []*models.CustomFronMatter) error
|
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
|
Delete(ctx context.Context, dbContext persistence.IDbContext, noteID string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,3 +58,34 @@ func (r *cfeRepository) Delete(
|
||||||
|
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
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.GetNotesByCfeHandler {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
@ -54,9 +54,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