Merge pull request #15 from KristianJBorgwarth/ref/link-model
feat(link_model): updated link model and added link repo
This commit is contained in:
commit
25098f0778
9 changed files with 131 additions and 39 deletions
|
|
@ -1,16 +1,42 @@
|
|||
package models
|
||||
|
||||
import "github.com/google/uuid"
|
||||
|
||||
type Link struct {
|
||||
id string
|
||||
fromNoteID string
|
||||
toNoteID string
|
||||
targetSlug string
|
||||
raw string
|
||||
display string
|
||||
line int
|
||||
col int
|
||||
}
|
||||
|
||||
func NewLink(fromNoteID, toNoteID, raw string) *Link {
|
||||
func NewLink(id, fromNoteID, targetSlug, raw, display string, line, col int) *Link {
|
||||
return &Link{
|
||||
id: id,
|
||||
fromNoteID: fromNoteID,
|
||||
toNoteID: toNoteID,
|
||||
targetSlug: targetSlug,
|
||||
raw: raw,
|
||||
display: display,
|
||||
line: line,
|
||||
col: col,
|
||||
}
|
||||
}
|
||||
|
||||
func CreateLink(fromNoteID, targetSlug, raw, display string, line, col int) *Link {
|
||||
id, err := uuid.NewV7()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &Link{
|
||||
id: id.String(),
|
||||
fromNoteID: fromNoteID,
|
||||
targetSlug: targetSlug,
|
||||
raw: raw,
|
||||
display: display,
|
||||
line: line,
|
||||
col: col,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -18,8 +44,8 @@ func (l *Link) FromNoteID() string {
|
|||
return l.fromNoteID
|
||||
}
|
||||
|
||||
func (l *Link) ToNoteID() string {
|
||||
return l.toNoteID
|
||||
func (l *Link) TargetSlug() string {
|
||||
return l.targetSlug
|
||||
}
|
||||
|
||||
func (l *Link) Raw() string {
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
CREATE TABLE IF NOT EXISTS notes (
|
||||
id TEXT PRIMARY KEY,
|
||||
path TEXT UNIQUE,
|
||||
title TEXT,
|
||||
slug TEXT UNIQUE NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS links (
|
||||
from_note_id TEXT NOT NULL,
|
||||
to_note_id TEXT NOT NULL,
|
||||
raw TEXT,
|
||||
FOREIGN KEY(from_note_id) REFERENCES notes(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(to_note_id) REFERENCES notes(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_notes_slug ON notes(slug);
|
||||
CREATE INDEX IF NOT EXISTS idx_links_from ON links(from_note_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_links_to ON links(to_note_id);
|
||||
|
||||
12
persistence/migrations/001_note.sql
Normal file
12
persistence/migrations/001_note.sql
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
CREATE TABLE IF NOT EXISTS note (
|
||||
id TEXT PRIMARY KEY,
|
||||
path TEXT UNIQUE,
|
||||
title TEXT,
|
||||
slug TEXT UNIQUE NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_note_slug ON note(slug);
|
||||
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
CREATE TABLE IF NOT EXISTS tags (
|
||||
CREATE TABLE IF NOT EXISTS tag (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT UNIQUE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS note_tags (
|
||||
CREATE TABLE IF NOT EXISTS note_tag (
|
||||
note_id TEXT,
|
||||
tag_id TEXT,
|
||||
PRIMARY KEY (note_id, tag_id),
|
||||
FOREIGN KEY(note_id) REFERENCES notes(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(tag_id) REFERENCES tags(id) ON DELETE CASCADE
|
||||
FOREIGN KEY(note_id) REFERENCES note(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(tag_id) REFERENCES tag(id) ON DELETE CASCADE
|
||||
);
|
||||
|
|
|
|||
14
persistence/migrations/003_link.sql
Normal file
14
persistence/migrations/003_link.sql
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
CREATE TABLE IF NOT EXISTS link (
|
||||
id TEXT PRIMARY KEY,
|
||||
from_note_id TEXT NOT NULL,
|
||||
target_slug TEXT NOT NULL,
|
||||
display TEXT,
|
||||
raw TEXT NOT NULL,
|
||||
line INTEGER NOT NULL,
|
||||
col INTEGER NOT NULL,
|
||||
FOREIGN KEY(from_note_id) REFERENCES note(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX idx_link_from ON link(from_note_id);
|
||||
CREATE INDEX idx_link_target ON link(target_slug);
|
||||
|
||||
61
persistence/repositories/link_repository.go
Normal file
61
persistence/repositories/link_repository.go
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"github.com/KristianJBorgwarth/dendrite.daemon/core/models"
|
||||
)
|
||||
|
||||
type ILinkRepository interface {
|
||||
GetByNoteID(ctx context.Context, fromNoteID string) ([]*models.Link, error)
|
||||
GetBySlug(ctx context.Context, targetSlug string) ([]*models.Link, error)
|
||||
}
|
||||
|
||||
type linkRepository struct {
|
||||
Transaction *sql.Tx
|
||||
}
|
||||
|
||||
func NewLinkRepository(tx *sql.Tx) ILinkRepository {
|
||||
return &linkRepository{Transaction: tx}
|
||||
}
|
||||
|
||||
func (r *linkRepository) GetByNoteID(ctx context.Context, fromNoteID string) ([]*models.Link, error) {
|
||||
rows, err := r.Transaction.QueryContext(ctx, "SELECT id, from_note_id, target_slug, raw, display, line, col FROM links WHERE from_note_id = ?", fromNoteID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var links []*models.Link
|
||||
for rows.Next() {
|
||||
var id, fromNoteID, targetSlug, raw, display string
|
||||
var line, col int
|
||||
if err := rows.Scan(&id, &fromNoteID, &targetSlug, &raw, &display, &line, &col); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
links = append(links, models.NewLink(id, fromNoteID, targetSlug, raw, display, line, col))
|
||||
}
|
||||
|
||||
return links, nil
|
||||
}
|
||||
|
||||
func (r *linkRepository) GetBySlug(ctx context.Context, targetSlug string) ([]*models.Link, error) {
|
||||
rows, err := r.Transaction.QueryContext(ctx, `SELECT id, from_note_id, target_slug, raw, display, line, col FROM links WHERE target_slug = ?`, targetSlug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var links []*models.Link
|
||||
for rows.Next() {
|
||||
var id, fromNoteID, targetSlug, raw, display string
|
||||
var line, col int
|
||||
if err := rows.Scan(&id, &fromNoteID, &targetSlug, &raw, &display, &line, &col); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
links = append(links, models.NewLink(id, fromNoteID, targetSlug, raw, display, line, col))
|
||||
}
|
||||
|
||||
return links, nil
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ func NewNoteRepository(tx *sql.Tx) NoteRepository {
|
|||
|
||||
func (r *noteRepository) Upsert(ctx context.Context, note *models.Note) error {
|
||||
query := `
|
||||
INSERT INTO notes (id, title, path, slug, created_at, updated_at)
|
||||
INSERT INTO note (id, title, path, slug, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, datetime('now'), datetime('now'))
|
||||
ON CONFLICT (slug) DO UPDATE
|
||||
SET title = EXCLUDED.title,
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ func (r *tagRepository) Upsert(ctx context.Context, tags []*models.Tag) error {
|
|||
args = append(args, tag.ID(), tag.Name())
|
||||
}
|
||||
|
||||
query := "INSERT OR IGNORE INTO tags(id, name) VALUES " + strings.Join(placeholders, ",")
|
||||
query := "INSERT OR IGNORE INTO tag(id, name) VALUES " + strings.Join(placeholders, ",")
|
||||
|
||||
_, err := r.Transaction.ExecContext(ctx, query, args...)
|
||||
return err
|
||||
|
|
@ -54,7 +54,7 @@ func (r *tagRepository) UpsertNoteTags(ctx context.Context ,noteID string, tagID
|
|||
args = append(args, noteID, tagID)
|
||||
}
|
||||
|
||||
query := "INSERT OR IGNORE INTO note_tags(note_id, tag_id) VALUES " + strings.Join(placeholders, ",")
|
||||
query := "INSERT OR IGNORE INTO note_tag(note_id, tag_id) VALUES " + strings.Join(placeholders, ",")
|
||||
|
||||
_, err := r.Transaction.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
|
|
@ -77,7 +77,7 @@ func (r *tagRepository) GetByNames(ctx context.Context, names []string) ([]*mode
|
|||
args = append(args, name)
|
||||
}
|
||||
|
||||
query := "SELECT id, name FROM tags WHERE name IN (" + strings.Join(placeholders, ",") + ")"
|
||||
query := "SELECT id, name FROM tag WHERE name IN (" + strings.Join(placeholders, ",") + ")"
|
||||
|
||||
rows, err := r.Transaction.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ func TestCreateNoteHandler_NoTemplate_CreatesNoteFileAndReturnsPath(t *testing.T
|
|||
assert.Equal(t, notePath, result)
|
||||
|
||||
var count int
|
||||
require.NoError(t, Fixture.DB.QueryRow(`SELECT COUNT(*) FROM notes WHERE slug = ?`, "my-note").Scan(&count))
|
||||
require.NoError(t, Fixture.DB.QueryRow(`SELECT COUNT(*) FROM note WHERE slug = ?`, "my-note").Scan(&count))
|
||||
assert.Equal(t, 1, count)
|
||||
|
||||
_, statErr := os.Stat(notePath)
|
||||
|
|
@ -68,10 +68,10 @@ func TestCreateNoteHandler_WithTemplate_CreatesNoteFileWithTagsAndReturnsPath(t
|
|||
assert.Equal(t, notePath, result)
|
||||
|
||||
var noteCount int
|
||||
require.NoError(t, Fixture.DB.QueryRow(`SELECT COUNT(*) FROM notes WHERE slug = ?`, "templated-note").Scan(¬eCount))
|
||||
require.NoError(t, Fixture.DB.QueryRow(`SELECT COUNT(*) FROM note WHERE slug = ?`, "templated-note").Scan(¬eCount))
|
||||
assert.Equal(t, 1, noteCount)
|
||||
|
||||
rows, err := Fixture.DB.Query(`SELECT name FROM tags WHERE name IN ('go', 'testing')`)
|
||||
rows, err := Fixture.DB.Query(`SELECT name FROM tag WHERE name IN ('go', 'testing')`)
|
||||
require.NoError(t, err)
|
||||
defer rows.Close()
|
||||
|
||||
|
|
@ -112,12 +112,12 @@ func TestCreateNoteHandler_DuplicateSlug_Upserts(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
var count int
|
||||
require.NoError(t, Fixture.DB.QueryRow(`SELECT COUNT(*) FROM notes WHERE slug = ?`, "dup-note").Scan(&count))
|
||||
require.NoError(t, Fixture.DB.QueryRow(`SELECT COUNT(*) FROM note WHERE slug = ?`, "dup-note").Scan(&count))
|
||||
assert.Equal(t, 1, count)
|
||||
|
||||
expectedPath := filepath.Join(vaultPath, subDir2, "dup-note.md")
|
||||
var path string
|
||||
require.NoError(t, Fixture.DB.QueryRow(`SELECT path FROM notes WHERE slug = ?`, "dup-note").Scan(&path))
|
||||
require.NoError(t, Fixture.DB.QueryRow(`SELECT path FROM note WHERE slug = ?`, "dup-note").Scan(&path))
|
||||
assert.Equal(t, expectedPath, path)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue