diff options
Diffstat (limited to 'storage/feed.go')
-rw-r--r-- | storage/feed.go | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/storage/feed.go b/storage/feed.go new file mode 100644 index 0000000..ec08580 --- /dev/null +++ b/storage/feed.go @@ -0,0 +1,223 @@ +// Copyright 2017 Frédéric Guillot. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package storage + +import ( + "database/sql" + "errors" + "fmt" + "github.com/miniflux/miniflux2/helper" + "github.com/miniflux/miniflux2/model" + "time" +) + +func (s *Storage) FeedExists(userID, feedID int64) bool { + defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:FeedExists] userID=%d, feedID=%d", userID, feedID)) + + var result int + query := `SELECT count(*) as c FROM feeds WHERE user_id=$1 AND id=$2` + s.db.QueryRow(query, userID, feedID).Scan(&result) + return result >= 1 +} + +func (s *Storage) FeedURLExists(userID int64, feedURL string) bool { + defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:FeedURLExists] userID=%d, feedURL=%s", userID, feedURL)) + + var result int + query := `SELECT count(*) as c FROM feeds WHERE user_id=$1 AND feed_url=$2` + s.db.QueryRow(query, userID, feedURL).Scan(&result) + return result >= 1 +} + +func (s *Storage) GetFeeds(userID int64) (model.Feeds, error) { + defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:GetFeeds] userID=%d", userID)) + + feeds := make(model.Feeds, 0) + query := `SELECT + f.id, f.feed_url, f.site_url, f.title, f.etag_header, f.last_modified_header, + f.user_id, f.checked_at, f.parsing_error_count, f.parsing_error_msg, + f.category_id, c.title as category_title, + fi.icon_id + FROM feeds f + LEFT JOIN categories c ON c.id=f.category_id + LEFT JOIN feed_icons fi ON fi.feed_id=f.id + WHERE f.user_id=$1 + ORDER BY f.id ASC` + + rows, err := s.db.Query(query, userID) + if err != nil { + return nil, fmt.Errorf("Unable to fetch feeds: %v", err) + } + defer rows.Close() + + for rows.Next() { + var feed model.Feed + var iconID, errorMsg interface{} + feed.Category = &model.Category{UserID: userID} + feed.Icon = &model.FeedIcon{} + + err := rows.Scan( + &feed.ID, + &feed.FeedURL, + &feed.SiteURL, + &feed.Title, + &feed.EtagHeader, + &feed.LastModifiedHeader, + &feed.UserID, + &feed.CheckedAt, + &feed.ParsingErrorCount, + &errorMsg, + &feed.Category.ID, + &feed.Category.Title, + &iconID, + ) + + if err != nil { + return nil, fmt.Errorf("Unable to fetch feeds row: %v", err) + } + + if iconID == nil { + feed.Icon.IconID = 0 + } else { + feed.Icon.IconID = iconID.(int64) + } + + if errorMsg == nil { + feed.ParsingErrorMsg = "" + } else { + feed.ParsingErrorMsg = errorMsg.(string) + } + + feed.Icon.FeedID = feed.ID + feeds = append(feeds, &feed) + } + + return feeds, nil +} + +func (s *Storage) GetFeedById(userID, feedID int64) (*model.Feed, error) { + defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:GetFeedById] feedID=%d", feedID)) + + var feed model.Feed + feed.Category = &model.Category{UserID: userID} + + query := ` + SELECT + f.id, f.feed_url, f.site_url, f.title, f.etag_header, f.last_modified_header, + f.user_id, f.checked_at, f.parsing_error_count, f.parsing_error_msg, + f.category_id, c.title as category_title + FROM feeds f + LEFT JOIN categories c ON c.id=f.category_id + WHERE f.user_id=$1 AND f.id=$2` + + err := s.db.QueryRow(query, userID, feedID).Scan( + &feed.ID, + &feed.FeedURL, + &feed.SiteURL, + &feed.Title, + &feed.EtagHeader, + &feed.LastModifiedHeader, + &feed.UserID, + &feed.CheckedAt, + &feed.ParsingErrorCount, + &feed.ParsingErrorMsg, + &feed.Category.ID, + &feed.Category.Title, + ) + + switch { + case err == sql.ErrNoRows: + return nil, nil + case err != nil: + return nil, fmt.Errorf("Unable to fetch feed: %v", err) + } + + return &feed, nil +} + +func (s *Storage) CreateFeed(feed *model.Feed) error { + defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:CreateFeed] feedURL=%s", feed.FeedURL)) + sql := ` + INSERT INTO feeds + (feed_url, site_url, title, category_id, user_id, etag_header, last_modified_header) + VALUES ($1, $2, $3, $4, $5, $6, $7) + RETURNING id + ` + + err := s.db.QueryRow( + sql, + feed.FeedURL, + feed.SiteURL, + feed.Title, + feed.Category.ID, + feed.UserID, + feed.EtagHeader, + feed.LastModifiedHeader, + ).Scan(&feed.ID) + + if err != nil { + return fmt.Errorf("Unable to create feed: %v", err) + } + + for i := 0; i < len(feed.Entries); i++ { + feed.Entries[i].FeedID = feed.ID + feed.Entries[i].UserID = feed.UserID + err := s.CreateEntry(feed.Entries[i]) + if err != nil { + return err + } + } + + return nil +} + +func (s *Storage) UpdateFeed(feed *model.Feed) (err error) { + defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:UpdateFeed] feedURL=%s", feed.FeedURL)) + + query := `UPDATE feeds SET + feed_url=$1, site_url=$2, title=$3, category_id=$4, etag_header=$5, last_modified_header=$6, checked_at=$7, + parsing_error_msg=$8, parsing_error_count=$9 + WHERE id=$10 AND user_id=$11` + + _, err = s.db.Exec(query, + feed.FeedURL, + feed.SiteURL, + feed.Title, + feed.Category.ID, + feed.EtagHeader, + feed.LastModifiedHeader, + feed.CheckedAt, + feed.ParsingErrorMsg, + feed.ParsingErrorCount, + feed.ID, + feed.UserID, + ) + + if err != nil { + return fmt.Errorf("Unable to update feed: %v", err) + } + + return nil +} + +func (s *Storage) RemoveFeed(userID, feedID int64) error { + defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:RemoveFeed] userID=%d, feedID=%d", userID, feedID)) + + result, err := s.db.Exec("DELETE FROM feeds WHERE id = $1 AND user_id = $2", feedID, userID) + if err != nil { + return fmt.Errorf("Unable to remove this feed: %v", err) + } + + count, err := result.RowsAffected() + if err != nil { + return fmt.Errorf("Unable to remove this feed: %v", err) + } + + if count == 0 { + return errors.New("no feed has been removed") + } + + return nil +} |