mirror of
https://github.com/nkanaev/yarr.git
synced 2026-06-25 09:35:15 +00:00
refactor feedstate + swap implementation
This commit is contained in:
@@ -30,7 +30,7 @@ func (s *Storage) CreateFeed(params CreateFeedParams) *Feed {
|
||||
title = params.FeedLink
|
||||
}
|
||||
row := s.db.QueryRow(`
|
||||
insert into feeds (title, description, link, feed_link, folder_id)
|
||||
insert into feeds (title, description, link, feed_link, folder_id)
|
||||
values (:title, :description, :link, :feed_link, :folder_id)
|
||||
on conflict (feed_link) do update set folder_id = :folder_id
|
||||
returning id`,
|
||||
@@ -185,42 +185,3 @@ func (s *Storage) GetFeed(id int64) *Feed {
|
||||
}
|
||||
return &f
|
||||
}
|
||||
|
||||
func (s *Storage) ResetFeedErrors() {
|
||||
if _, err := s.db.Exec(`delete from feed_errors`); err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Storage) SetFeedError(feedID int64, lastError error) {
|
||||
_, err := s.db.Exec(`
|
||||
insert into feed_errors (feed_id, error)
|
||||
values (:feed_id, :error)
|
||||
on conflict (feed_id) do update set error = excluded.error`,
|
||||
sql.Named("feed_id", feedID),
|
||||
sql.Named("error", lastError.Error()),
|
||||
)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Storage) GetFeedErrors() map[int64]string {
|
||||
errors := make(map[int64]string)
|
||||
|
||||
rows, err := s.db.Query(`select feed_id, error from feed_errors`)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return errors
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var id int64
|
||||
var error string
|
||||
if err = rows.Scan(&id, &error); err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
errors[id] = error
|
||||
}
|
||||
return errors
|
||||
}
|
||||
|
||||
@@ -8,14 +8,19 @@ import (
|
||||
type FeedState struct {
|
||||
FeedID int64
|
||||
LastRefreshed time.Time
|
||||
LastError *string
|
||||
LastError string
|
||||
HTTPLastModified string
|
||||
HTTPEtag string
|
||||
}
|
||||
|
||||
func (s *Storage) ListFeedStates() ([]FeedState, error) {
|
||||
rows, err := s.db.Query(`
|
||||
select feed_id, last_refreshed, last_modified, etag, last_error
|
||||
select
|
||||
feed_id
|
||||
, last_refreshed
|
||||
, last_error
|
||||
, http_lmod
|
||||
, http_etag
|
||||
from feed_states
|
||||
`)
|
||||
if err != nil {
|
||||
@@ -29,9 +34,9 @@ func (s *Storage) ListFeedStates() ([]FeedState, error) {
|
||||
err := rows.Scan(
|
||||
&state.FeedID,
|
||||
&state.LastRefreshed,
|
||||
&state.LastError,
|
||||
&state.HTTPLastModified,
|
||||
&state.HTTPEtag,
|
||||
&state.LastError,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -44,14 +49,19 @@ func (s *Storage) ListFeedStates() ([]FeedState, error) {
|
||||
func (s *Storage) GetFeedState(feedID int64) (*FeedState, error) {
|
||||
var state FeedState
|
||||
err := s.db.QueryRow(`
|
||||
select feed_id, last_refreshed, last_modified, etag, last_error
|
||||
select
|
||||
feed_id
|
||||
, last_refreshed
|
||||
, last_error
|
||||
, http_lmod
|
||||
, http_etag
|
||||
from feed_states where feed_id = :id
|
||||
`, sql.Named("id", feedID)).Scan(
|
||||
&state.FeedID,
|
||||
&state.LastRefreshed,
|
||||
&state.LastError,
|
||||
&state.HTTPLastModified,
|
||||
&state.HTTPEtag,
|
||||
&state.LastError,
|
||||
)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
@@ -79,28 +89,28 @@ func (s *Storage) UpdateFeedState(feedID int64, params UpdateFeedStateParams) (b
|
||||
insert into feed_states (
|
||||
feed_id
|
||||
, last_refreshed
|
||||
, last_modified
|
||||
, etag
|
||||
, last_error
|
||||
, http_lmod
|
||||
, http_etag
|
||||
)
|
||||
values (
|
||||
:id
|
||||
, coalesce(:refreshed, 0)
|
||||
, coalesce(:last_modified, '')
|
||||
, coalesce(:etag, '')
|
||||
, coalesce(:last_refreshed, 0)
|
||||
, coalesce(:last_error, '')
|
||||
, coalesce(:http_lmod, '')
|
||||
, coalesce(:http_etag, '')
|
||||
)
|
||||
on conflict (feed_id) do update set
|
||||
last_refreshed = coalesce(:refreshed, last_refreshed),
|
||||
last_modified = coalesce(:last_modified, last_modified),
|
||||
etag = coalesce(:etag, etag),
|
||||
last_error = coalesce(:last_error, last_error)
|
||||
last_refreshed = coalesce(:last_refreshed, last_refreshed),
|
||||
last_error = coalesce(:last_error, last_modified),
|
||||
http_lmod = coalesce(:http_lmod, http_lmod),
|
||||
http_etag = coalesce(:http_etag, http_etag)
|
||||
`,
|
||||
sql.Named("id", feedID),
|
||||
sql.Named("refreshed", params.LastRefreshed),
|
||||
sql.Named("last_modified", params.HTTPLastModified),
|
||||
sql.Named("etag", params.HTTPEtag),
|
||||
sql.Named("last_refreshed", params.LastRefreshed),
|
||||
sql.Named("last_error", params.LastError),
|
||||
sql.Named("http_lmod", params.HTTPLastModified),
|
||||
sql.Named("http_etag", params.HTTPEtag),
|
||||
)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
||||
@@ -39,7 +39,7 @@ func TestUpdateFeedState_Full(t *testing.T) {
|
||||
if !state.LastRefreshed.Equal(now) {
|
||||
t.Errorf("expected %v, got %v", now, state.LastRefreshed)
|
||||
}
|
||||
if state.LastError == nil || *state.LastError != errMsg {
|
||||
if state.LastError != errMsg {
|
||||
t.Errorf("expected %s, got %v", errMsg, state.LastError)
|
||||
}
|
||||
if state.HTTPLastModified != lmod {
|
||||
@@ -70,7 +70,7 @@ func TestUpdateFeedState_Partial(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if state.LastError == nil || *state.LastError != newErr {
|
||||
if state.LastError != newErr {
|
||||
t.Errorf("expected %s, got %v", newErr, state.LastError)
|
||||
}
|
||||
if state.HTTPEtag != etag {
|
||||
@@ -98,8 +98,8 @@ func TestUpdateFeedState_ClearError(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if state.LastError == nil || *state.LastError != "" {
|
||||
t.Errorf("expected empty string error, got %v", state.LastError)
|
||||
if state.LastError != "" {
|
||||
t.Errorf("expected empty error string, got %v", state.LastError)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,9 +111,8 @@ func TestListFeedStates(t *testing.T) {
|
||||
f2 := s.CreateFeed(CreateFeedParams{Title: "F2", FeedLink: "L2"})
|
||||
|
||||
errMsg := "fail"
|
||||
etag := "e"
|
||||
s.UpdateFeedState(f1.Id, UpdateFeedStateParams{LastError: &errMsg})
|
||||
s.UpdateFeedState(f2.Id, UpdateFeedStateParams{HTTPEtag: &etag})
|
||||
s.UpdateFeedState(f2.Id, UpdateFeedStateParams{HTTPEtag: ptr("e")})
|
||||
|
||||
states, err := s.ListFeedStates()
|
||||
if err != nil {
|
||||
@@ -124,3 +123,7 @@ func TestListFeedStates(t *testing.T) {
|
||||
t.Errorf("expected 2 states, got %d", len(states))
|
||||
}
|
||||
}
|
||||
|
||||
func ptr[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
type HTTPState struct {
|
||||
FeedID int64
|
||||
LastRefreshed time.Time
|
||||
|
||||
LastModified string
|
||||
Etag string
|
||||
}
|
||||
|
||||
func (s *Storage) ListHTTPStates() map[int64]HTTPState {
|
||||
result := make(map[int64]HTTPState)
|
||||
rows, err := s.db.Query(`select feed_id, last_refreshed, last_modified, etag from http_states`)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return result
|
||||
}
|
||||
for rows.Next() {
|
||||
var state HTTPState
|
||||
err = rows.Scan(
|
||||
&state.FeedID,
|
||||
&state.LastRefreshed,
|
||||
&state.LastModified,
|
||||
&state.Etag,
|
||||
)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return result
|
||||
}
|
||||
result[state.FeedID] = state
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *Storage) GetHTTPState(feedID int64) *HTTPState {
|
||||
row := s.db.QueryRow(`
|
||||
select feed_id, last_refreshed, last_modified, etag
|
||||
from http_states where feed_id = :feed_id
|
||||
`, sql.Named("feed_id", feedID))
|
||||
|
||||
if row == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var state HTTPState
|
||||
row.Scan(
|
||||
&state.FeedID,
|
||||
&state.LastRefreshed,
|
||||
&state.LastModified,
|
||||
&state.Etag,
|
||||
)
|
||||
return &state
|
||||
}
|
||||
|
||||
func (s *Storage) SetHTTPState(feedID int64, lastModified, etag string) {
|
||||
_, err := s.db.Exec(`
|
||||
insert into http_states (feed_id, last_modified, etag, last_refreshed)
|
||||
values (:feed_id, :last_modified, :etag, datetime())
|
||||
on conflict (feed_id) do update set last_modified = :last_modified, etag = :etag, last_refreshed = datetime()`,
|
||||
sql.Named("feed_id", feedID),
|
||||
sql.Named("last_modified", lastModified),
|
||||
sql.Named("etag", etag),
|
||||
)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
}
|
||||
@@ -350,20 +350,27 @@ func m12_remove_feed_sizes(tx *sql.Tx) error {
|
||||
func m13_consolidate_feed_states(tx *sql.Tx) error {
|
||||
sql := `
|
||||
create table feed_states (
|
||||
feed_id references feeds(id) on delete cascade unique,
|
||||
last_refreshed datetime not null default 0,
|
||||
last_modified string not null default '',
|
||||
etag string not null default '',
|
||||
last_error string not null default ''
|
||||
feed_id references feeds(id) on delete cascade unique
|
||||
, last_refreshed datetime not null default 0
|
||||
, last_error string not null default ''
|
||||
|
||||
, http_lmod string not null default ''
|
||||
, http_etag string not null default ''
|
||||
);
|
||||
|
||||
insert into feed_states (feed_id, last_refreshed, last_modified, etag, last_error)
|
||||
insert into feed_states (
|
||||
feed_id
|
||||
, last_refreshed
|
||||
, last_error
|
||||
, http_lmod
|
||||
, http_etag
|
||||
)
|
||||
select
|
||||
f.id,
|
||||
coalesce(h.last_refreshed, 0),
|
||||
coalesce(e.error, '')
|
||||
coalesce(h.last_modified, ''),
|
||||
coalesce(h.etag, ''),
|
||||
coalesce(e.error, '')
|
||||
from feeds f
|
||||
left join http_states h on f.id = h.feed_id
|
||||
left join feed_errors e on f.id = e.feed_id
|
||||
|
||||
Reference in New Issue
Block a user