feat(note/backlinks): added support for querying backlinks (#33)
* feat(link/backlinks): backlinks query * feat(link/backlinks): register handler * feat(note/backlinks): added backlink query and handler
This commit is contained in:
parent
594764344d
commit
fda0286690
7 changed files with 111 additions and 0 deletions
|
|
@ -37,6 +37,7 @@ func main() {
|
|||
server.RegisterHandler("note/create", note.NewCreateNoteHandler(uow, tagService, noteRepo))
|
||||
server.RegisterHandler("note/save", note.NewSaveNoteHandler(uow, noteRepo, tagService, noteService, linkService))
|
||||
server.RegisterHandler("note/goto", note.NewGotoNoteHandler(noteRepo))
|
||||
server.RegisterHandler("note/backlinks", note.NewGetBackLinksHandler(linkRepo, noteRepo))
|
||||
|
||||
server.RegisterHandler("completion/link", completion.NewCompleteLinkHandler(linkRepo))
|
||||
server.RegisterHandler("completion/tag", completion.NewCompleteTagHandler(tagRepo))
|
||||
|
|
|
|||
23
core/dtos/backlink.go
Normal file
23
core/dtos/backlink.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package dtos
|
||||
|
||||
type Backlink struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Slug string `json:"slug"`
|
||||
Path string `json:"path"`
|
||||
Raw string `json:"raw"`
|
||||
Line int `json:"line"`
|
||||
Col int `json:"col"`
|
||||
}
|
||||
|
||||
func NewBacklink(id, title, slug, path, raw string, col, line int) *Backlink {
|
||||
return &Backlink{
|
||||
ID: id,
|
||||
Title: title,
|
||||
Slug: slug,
|
||||
Path: path,
|
||||
Raw: raw,
|
||||
Line: line,
|
||||
Col: col,
|
||||
}
|
||||
}
|
||||
2
core/dtos/doc.go
Normal file
2
core/dtos/doc.go
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// Package dtos contains the data transfer objects used in the application.
|
||||
package dtos
|
||||
40
core/handlers/note/get_backlinks_handler.go
Normal file
40
core/handlers/note/get_backlinks_handler.go
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
package note
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/KristianJBorgwarth/dendrite.daemon/persistence/repositories"
|
||||
)
|
||||
|
||||
type getBacklinksCommand struct {
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
type GetBackLinksHandler struct {
|
||||
linkRepo repositories.ILinkRepository
|
||||
noteRepo repositories.INoteRepository
|
||||
}
|
||||
|
||||
func NewGetBackLinksHandler(lr repositories.ILinkRepository, nr repositories.INoteRepository) *GetBackLinksHandler {
|
||||
return &GetBackLinksHandler{linkRepo: lr, noteRepo: nr}
|
||||
}
|
||||
|
||||
func (h *GetBackLinksHandler) Handle(ctx context.Context, raw json.RawMessage) (any, error) {
|
||||
var cmd getBacklinksCommand
|
||||
if err := json.Unmarshal(raw, &cmd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
note, err := h.noteRepo.GetByPath(ctx, cmd.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
links, err := h.linkRepo.GetBacklinks(ctx, note.Slug())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return links, nil
|
||||
}
|
||||
BIN
dendrite
BIN
dendrite
Binary file not shown.
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/KristianJBorgwarth/dendrite.daemon/core/dtos"
|
||||
"github.com/KristianJBorgwarth/dendrite.daemon/core/models"
|
||||
"github.com/KristianJBorgwarth/dendrite.daemon/persistence"
|
||||
)
|
||||
|
|
@ -14,6 +15,7 @@ type ILinkRepository interface {
|
|||
GetByNoteID(ctx context.Context, dbContext persistence.IDbContext, fromNoteID string) ([]*models.Link, error)
|
||||
GetBySlug(ctx context.Context, dbContext persistence.IDbContext, targetSlug string) ([]*models.Link, error)
|
||||
Search(ctx context.Context, query string) ([]*models.Link, error)
|
||||
GetBacklinks(ctx context.Context, slug string) ([]*dtos.Backlink, error)
|
||||
Delete(ctx context.Context, dbContext persistence.IDbContext, fromNoteID string) error
|
||||
}
|
||||
|
||||
|
|
@ -123,6 +125,32 @@ func (r *linkRepository) Search(ctx context.Context, query string) ([]*models.Li
|
|||
return links, nil
|
||||
}
|
||||
|
||||
func (r *linkRepository) GetBacklinks(ctx context.Context, slug string) ([]*dtos.Backlink, error) {
|
||||
query := `
|
||||
SELECT n.id, n.slug, n.title, n.path, l.raw, l.line, l.col
|
||||
FROM link l
|
||||
JOIN note n ON l.from_note_id = n.id
|
||||
WHERE l.target_slug = ?`
|
||||
|
||||
rows, err := r.readDBContext.QueryContext(ctx, query, slug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
backlinks := make([]*dtos.Backlink, 0)
|
||||
for rows.Next() {
|
||||
var noteID, noteSlug, noteTitle, notePath, raw string
|
||||
var line, col int
|
||||
if err := rows.Scan(¬eID, ¬eSlug, ¬eTitle, ¬ePath, &raw, &line, &col); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
backlinks = append(backlinks, dtos.NewBacklink(noteID, noteTitle, noteSlug, notePath, raw, line, col))
|
||||
}
|
||||
|
||||
return backlinks, nil
|
||||
}
|
||||
|
||||
func (r *linkRepository) Delete(ctx context.Context, dbContext persistence.IDbContext, fromNoteID string) error {
|
||||
_, err := dbContext.ExecContext(ctx, "DELETE FROM link WHERE from_note_id = ?", fromNoteID)
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ type INoteRepository interface {
|
|||
InsertRange(ctx context.Context, dbContext persistence.IDbContext, note []*models.Note) error
|
||||
Update(ctx context.Context, dbContext persistence.IDbContext, noteID, path, title, slug string) error
|
||||
GetBySlug(ctx context.Context, slug string) (*models.Note, error)
|
||||
GetByPath(ctx context.Context, path string) (*models.Note, error)
|
||||
GetNoteCount(ctx context.Context) (int, error)
|
||||
}
|
||||
|
||||
|
|
@ -84,6 +85,22 @@ func (r *noteRepository) GetBySlug(ctx context.Context, slug string) (*models.No
|
|||
return models.NewNote(id, path, title, slug, createdAt, updatedAt), nil
|
||||
}
|
||||
|
||||
func (r *noteRepository) GetByPath(ctx context.Context, path string) (*models.Note, error) {
|
||||
query := `SELECT id, title, path, slug, created_at, updated_at FROM note WHERE path = ?`
|
||||
row := r.readDBContext.QueryRowContext(ctx, query, path)
|
||||
|
||||
var id, title, slug, createdAt, updatedAt string
|
||||
err := row.Scan(&id, &title, &path, &slug, &createdAt, &updatedAt)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return models.NewNote(id, path, title, slug, createdAt, updatedAt), nil
|
||||
}
|
||||
|
||||
func (r *noteRepository) GetNoteCount(ctx context.Context) (int, error) {
|
||||
query := `SELECT COUNT(*) FROM note`
|
||||
row := r.readDBContext.QueryRowContext(ctx, query)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue