From 19b6d180e71c1caeb3279c5f1beede7666fb7652 Mon Sep 17 00:00:00 2001 From: Bel LaPointe <153096461+breel-render@users.noreply.github.com> Date: Sun, 27 Apr 2025 11:49:20 -0600 Subject: [PATCH] feeds from empty struct --- src/feeds/db.go | 422 ++++++++++++++++++++++--------------------- src/feeds/db_test.go | 41 +---- 2 files changed, 227 insertions(+), 236 deletions(-) diff --git a/src/feeds/db.go b/src/feeds/db.go index 3448e02..b3e60a4 100644 --- a/src/feeds/db.go +++ b/src/feeds/db.go @@ -11,10 +11,227 @@ import ( "github.com/google/uuid" ) -type Feeds struct{} +type ( + Feed struct { + Entry Entry + Version Version + Execution Execution + } -func New(ctx context.Context) (Feeds, error) { - return Feeds{}, initDB(ctx) + 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 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 { @@ -72,202 +289,3 @@ func initDB(ctx context.Context) error { 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) -} diff --git a/src/feeds/db_test.go b/src/feeds/db_test.go index a6eeeb0..aabbd39 100644 --- a/src/feeds/db_test.go +++ b/src/feeds/db_test.go @@ -4,7 +4,6 @@ import ( "context" "show-rss/src/db" "show-rss/src/feeds" - "strconv" "testing" "time" ) @@ -13,41 +12,15 @@ func TestFeeds(t *testing.T) { ctx, can := context.WithTimeout(context.Background(), 5*time.Second) 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) { ctx := db.Test(t, ctx) - f, err := feeds.New(ctx) - if err != nil && ctx.Err() == nil { - t.Fatalf("failed: %v", err) - } - - id, err := f.Insert(ctx, "url", "cron") + id, err := feeds.Insert(ctx, "url", "cron") if err != nil { t.Fatal("cannot insert:", err) } - got, err := f.Get(ctx, id) + got, err := feeds.Get(ctx, id) if err != nil { t.Fatal("cannot get:", err) } @@ -83,11 +56,11 @@ func TestFeeds(t *testing.T) { 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) } - got2, err := f.Get(ctx, id) + got2, err := feeds.Get(ctx, id) if err != nil { 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) } - 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) } - got3, err := f.Get(ctx, id) + got3, err := feeds.Get(ctx, id) if err != nil { t.Fatal("cannot get w executed again:", err) } else if got2.Execution == got3.Execution { @@ -116,7 +89,7 @@ func TestFeeds(t *testing.T) { } n := 0 - if err := f.ForEach(ctx, func(feed feeds.Feed) error { + if err := feeds.ForEach(ctx, func(feed feeds.Feed) error { n += 1 if feed != got3 { t.Errorf("for each yielded difference than last get")