feeds from empty struct

main
Bel LaPointe 2025-04-27 11:49:20 -06:00
parent e54c7a76f9
commit 19b6d180e7
2 changed files with 227 additions and 236 deletions

View File

@ -11,10 +11,227 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
) )
type Feeds struct{} type (
Feed struct {
Entry Entry
Version Version
Execution Execution
}
func New(ctx context.Context) (Feeds, error) { Entry struct {
return Feeds{}, initDB(ctx) ID string
Created time.Time
Updated time.Time
Deleted time.Time
}
Version struct {
Created time.Time
URL string
Cron string
}
Execution struct {
Executed time.Time
Version time.Time
}
)
func ForEach(ctx context.Context, cb func(Feed) error) error {
if err := initDB(ctx); err != nil {
return err
}
type id struct {
ID string `json:"id"`
}
ids, err := db.Query[id](ctx, `SELECT id FROM "feed.entries" WHERE deleted_at IS NULL`)
if err != nil {
return err
}
for _, id := range ids {
feed, err := Get(ctx, id.ID)
if err != nil {
return err
} else if err := cb(feed); err != nil {
return err
}
}
return nil
}
func Get(ctx context.Context, id string) (Feed, error) {
if err := initDB(ctx); err != nil {
return Feed{}, err
}
return db.QueryOne[Feed](ctx, `
WITH
entry AS (
SELECT
id AS ID,
created_at AS Created,
updated_at AS Updated,
deleted_at AS Deleted
FROM "feed.entries"
WHERE id = ?
),
execution AS (
SELECT
executed_at AS Executed,
versions_created_at AS Version
FROM "feed.executions"
WHERE entries_id = ?
ORDER BY executed DESC
LIMIT 1
)
SELECT
entry.ID AS "Entry.ID",
entry.Created AS "Entry.Created",
entry.Updated AS "Entry.Updated",
entry.Deleted AS "Entry.Deleted",
versions.created_at AS "Version.Created",
versions.url AS "Version.URL",
versions.cron AS "Version.Cron",
(
SELECT executed_at
FROM "feed.executions"
WHERE entries_id = entry.ID
ORDER BY executed_at DESC
LIMIT 1
) AS "Execution.Executed",
(
SELECT versions_created_at
FROM "feed.executions"
WHERE entries_id = entry.ID
ORDER BY executed_at DESC
LIMIT 1
) AS "Execution.Version"
FROM entry
JOIN "feed.versions" version_entries_id ON
version_entries_id.entries_id=entry.ID
JOIN "feed.versions" versions ON
versions.created_at=entry.Updated
`, id, id)
}
func oldGet(ctx context.Context, id string) (Feed, error) {
if err := initDB(ctx); err != nil {
return Feed{}, err
}
entry, err := getEntry(ctx, id)
if err != nil {
return Feed{}, err
}
version, err := db.QueryOne[Version](ctx, `
SELECT
"feed.current_versions".versions_created_at AS Created,
"feed.current_versions" AS URL,
"feed.current_versions" AS Cron
FROM
"feed.current_versions"
JOIN
"feed.versions" versions_a ON
"feed.current_versions".entries_id=versions_a.entries_id
JOIN
"feed.versions" versions_b ON
"feed.current_versions".versions_created_at=versions_b.created_at
WHERE
"feed.current_versions".entries_id = ?
`, id)
if err != nil {
return Feed{}, err
}
execution, err := db.QueryOne[Execution](ctx, `
SELECT
"feed.executed_at" AS Executed,
"feed.versions_created_at" AS VersionsCreated
FROM
"feed.executions"
WHERE
"feed.executions".entries_id = ?
ORDER BY "feed.executions".executed_at DESC
`, id)
if err != nil {
return Feed{}, err
}
return Feed{}, fmt.Errorf("%+v, %+v, %+v", entry, version, execution)
}
func Executed(ctx context.Context, id string, version time.Time) error {
if err := initDB(ctx); err != nil {
return err
}
now := time.Now()
return db.Exec(ctx, `
INSERT INTO "feed.executions" (
entries_id,
versions_created_at,
executed_at
) VALUES (?, ?, ?);
`,
id, version, now,
)
}
func Insert(ctx context.Context, url, cron string) (string, error) {
if err := initDB(ctx); err != nil {
return "", err
}
now := time.Now()
id := uuid.New().String()
return id, db.Exec(ctx, `
BEGIN;
INSERT INTO "feed.entries" (
id,
created_at,
updated_at
) VALUES ($1, $2, $3);
INSERT INTO "feed.versions" (
entries_id,
created_at,
url,
cron
) VALUES ($4, $5, $6, $7);
COMMIT;
`,
id, now, now,
id, now, url, cron,
)
}
func Update(ctx context.Context, id string, url, cron *string) error {
return io.EOF
}
func Delete(ctx context.Context, id string) error {
return io.EOF
}
func getEntry(ctx context.Context, id string) (Entry, error) {
if err := initDB(ctx); err != nil {
return Entry{}, err
}
return db.QueryOne[Entry](ctx, `
SELECT
id AS ID,
created_at AS Created,
updated_at AS Updated,
deleted_at AS Deleted
FROM
"feed.entries"
WHERE
id = ?
`, id)
} }
func initDB(ctx context.Context) error { func initDB(ctx context.Context) error {
@ -72,202 +289,3 @@ func initDB(ctx context.Context) error {
return nil return nil
} }
type (
Feed struct {
Entry Entry
Version Version
Execution Execution
}
Entry struct {
ID string
Created time.Time
Updated time.Time
Deleted time.Time
}
Version struct {
Created time.Time
URL string
Cron string
}
Execution struct {
Executed time.Time
Version time.Time
}
)
func (f *Feeds) ForEach(ctx context.Context, cb func(Feed) error) error {
type id struct {
ID string `json:"id"`
}
ids, err := db.Query[id](ctx, `SELECT id FROM "feed.entries" WHERE deleted_at IS NULL`)
if err != nil {
return err
}
for _, id := range ids {
feed, err := f.Get(ctx, id.ID)
if err != nil {
return err
} else if err := cb(feed); err != nil {
return err
}
}
return nil
}
func (f *Feeds) Get(ctx context.Context, id string) (Feed, error) {
return db.QueryOne[Feed](ctx, `
WITH
entry AS (
SELECT
id AS ID,
created_at AS Created,
updated_at AS Updated,
deleted_at AS Deleted
FROM "feed.entries"
WHERE id = ?
),
execution AS (
SELECT
executed_at AS Executed,
versions_created_at AS Version
FROM "feed.executions"
WHERE entries_id = ?
ORDER BY executed DESC
LIMIT 1
)
SELECT
entry.ID AS "Entry.ID",
entry.Created AS "Entry.Created",
entry.Updated AS "Entry.Updated",
entry.Deleted AS "Entry.Deleted",
versions.created_at AS "Version.Created",
versions.url AS "Version.URL",
versions.cron AS "Version.Cron",
(
SELECT executed_at
FROM "feed.executions"
WHERE entries_id = entry.ID
ORDER BY executed_at DESC
LIMIT 1
) AS "Execution.Executed",
(
SELECT versions_created_at
FROM "feed.executions"
WHERE entries_id = entry.ID
ORDER BY executed_at DESC
LIMIT 1
) AS "Execution.Version"
FROM entry
JOIN "feed.versions" version_entries_id ON
version_entries_id.entries_id=entry.ID
JOIN "feed.versions" versions ON
versions.created_at=entry.Updated
`, id, id)
}
func (f *Feeds) oldGet(ctx context.Context, id string) (Feed, error) {
entry, err := f.getEntry(ctx, id)
if err != nil {
return Feed{}, err
}
version, err := db.QueryOne[Version](ctx, `
SELECT
"feed.current_versions".versions_created_at AS Created,
"feed.current_versions" AS URL,
"feed.current_versions" AS Cron
FROM
"feed.current_versions"
JOIN
"feed.versions" versions_a ON
"feed.current_versions".entries_id=versions_a.entries_id
JOIN
"feed.versions" versions_b ON
"feed.current_versions".versions_created_at=versions_b.created_at
WHERE
"feed.current_versions".entries_id = ?
`, id)
if err != nil {
return Feed{}, err
}
execution, err := db.QueryOne[Execution](ctx, `
SELECT
"feed.executed_at" AS Executed,
"feed.versions_created_at" AS VersionsCreated
FROM
"feed.executions"
WHERE
"feed.executions".entries_id = ?
ORDER BY "feed.executions".executed_at DESC
`, id)
if err != nil {
return Feed{}, err
}
return Feed{}, fmt.Errorf("%+v, %+v, %+v", entry, version, execution)
}
func (f *Feeds) Executed(ctx context.Context, id string, version time.Time) error {
now := time.Now()
return db.Exec(ctx, `
INSERT INTO "feed.executions" (
entries_id,
versions_created_at,
executed_at
) VALUES (?, ?, ?);
`,
id, version, now,
)
}
func (f *Feeds) Insert(ctx context.Context, url, cron string) (string, error) {
now := time.Now()
id := uuid.New().String()
return id, db.Exec(ctx, `
BEGIN;
INSERT INTO "feed.entries" (
id,
created_at,
updated_at
) VALUES ($1, $2, $3);
INSERT INTO "feed.versions" (
entries_id,
created_at,
url,
cron
) VALUES ($4, $5, $6, $7);
COMMIT;
`,
id, now, now,
id, now, url, cron,
)
}
func (f *Feeds) Update(ctx context.Context, id string, url, cron *string) error {
return io.EOF
}
func (f *Feeds) Delete(ctx context.Context, id string) error {
return io.EOF
}
func (f *Feeds) getEntry(ctx context.Context, id string) (Entry, error) {
return db.QueryOne[Entry](ctx, `
SELECT
id AS ID,
created_at AS Created,
updated_at AS Updated,
deleted_at AS Deleted
FROM
"feed.entries"
WHERE
id = ?
`, id)
}

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"show-rss/src/db" "show-rss/src/db"
"show-rss/src/feeds" "show-rss/src/feeds"
"strconv"
"testing" "testing"
"time" "time"
) )
@ -13,41 +12,15 @@ func TestFeeds(t *testing.T) {
ctx, can := context.WithTimeout(context.Background(), 5*time.Second) ctx, can := context.WithTimeout(context.Background(), 5*time.Second)
defer can() defer can()
t.Run("same ctx", func(t *testing.T) {
ctx := db.Test(t, ctx)
for i := 0; i < 2; i++ {
t.Run(strconv.Itoa(i), func(t *testing.T) {
if _, err := feeds.New(ctx); err != nil && ctx.Err() == nil {
t.Fatalf("failed %d: %v", i, err)
}
})
}
})
t.Run("new ctx", func(t *testing.T) {
for i := 0; i < 2; i++ {
t.Run(strconv.Itoa(i), func(t *testing.T) {
if _, err := feeds.New(db.Test(t, ctx)); err != nil && ctx.Err() == nil {
t.Fatalf("failed %d: %v", i, err)
}
})
}
})
t.Run("crud", func(t *testing.T) { t.Run("crud", func(t *testing.T) {
ctx := db.Test(t, ctx) ctx := db.Test(t, ctx)
f, err := feeds.New(ctx) id, err := feeds.Insert(ctx, "url", "cron")
if err != nil && ctx.Err() == nil {
t.Fatalf("failed: %v", err)
}
id, err := f.Insert(ctx, "url", "cron")
if err != nil { if err != nil {
t.Fatal("cannot insert:", err) t.Fatal("cannot insert:", err)
} }
got, err := f.Get(ctx, id) got, err := feeds.Get(ctx, id)
if err != nil { if err != nil {
t.Fatal("cannot get:", err) t.Fatal("cannot get:", err)
} }
@ -83,11 +56,11 @@ func TestFeeds(t *testing.T) {
t.Error("execution.version") t.Error("execution.version")
} }
if err := f.Executed(ctx, got.Entry.ID, got.Version.Created); err != nil { if err := feeds.Executed(ctx, got.Entry.ID, got.Version.Created); err != nil {
t.Fatal("cannot executed:", err) t.Fatal("cannot executed:", err)
} }
got2, err := f.Get(ctx, id) got2, err := feeds.Get(ctx, id)
if err != nil { if err != nil {
t.Fatal("cannot get w executed:", err) t.Fatal("cannot get w executed:", err)
} }
@ -105,10 +78,10 @@ func TestFeeds(t *testing.T) {
t.Errorf("changes after execution: was \n\t%+v but now \n\t%+v", got, got2) t.Errorf("changes after execution: was \n\t%+v but now \n\t%+v", got, got2)
} }
if err := f.Executed(ctx, got.Entry.ID, got.Version.Created); err != nil { if err := feeds.Executed(ctx, got.Entry.ID, got.Version.Created); err != nil {
t.Fatal("cannot executed again:", err) t.Fatal("cannot executed again:", err)
} }
got3, err := f.Get(ctx, id) got3, err := feeds.Get(ctx, id)
if err != nil { if err != nil {
t.Fatal("cannot get w executed again:", err) t.Fatal("cannot get w executed again:", err)
} else if got2.Execution == got3.Execution { } else if got2.Execution == got3.Execution {
@ -116,7 +89,7 @@ func TestFeeds(t *testing.T) {
} }
n := 0 n := 0
if err := f.ForEach(ctx, func(feed feeds.Feed) error { if err := feeds.ForEach(ctx, func(feed feeds.Feed) error {
n += 1 n += 1
if feed != got3 { if feed != got3 {
t.Errorf("for each yielded difference than last get") t.Errorf("for each yielded difference than last get")