summaryrefslogtreecommitdiffhomepage
path: root/backend/graphql/resolvers.go
diff options
context:
space:
mode:
Diffstat (limited to 'backend/graphql/resolvers.go')
-rw-r--r--backend/graphql/resolvers.go341
1 files changed, 0 insertions, 341 deletions
diff --git a/backend/graphql/resolvers.go b/backend/graphql/resolvers.go
deleted file mode 100644
index 3e73162..0000000
--- a/backend/graphql/resolvers.go
+++ /dev/null
@@ -1,341 +0,0 @@
-package graphql
-
-// THIS CODE WILL BE UPDATED WITH SCHEMA CHANGES. PREVIOUS IMPLEMENTATION FOR SCHEMA CHANGES WILL BE KEPT IN THE COMMENT SECTION. IMPLEMENTATION FOR UNCHANGED SCHEMA WILL BE KEPT.
-
-import (
- "context"
- "database/sql"
- "fmt"
- "strconv"
- "time"
-
- "github.com/mmcdole/gofeed"
-
- "undef.ninja/x/feedaka/graphql/model"
-)
-
-type Resolver struct {
- DB *sql.DB
-}
-
-// AddFeed is the resolver for the addFeed field.
-func (r *mutationResolver) AddFeed(ctx context.Context, url string) (*model.Feed, error) {
- // Fetch the feed to get its title
- fp := gofeed.NewParser()
- feed, err := fp.ParseURL(url)
- if err != nil {
- return nil, fmt.Errorf("failed to parse feed: %w", err)
- }
-
- // Insert the feed into the database
- result, err := r.DB.Exec(
- "INSERT INTO feeds (url, title, fetched_at) VALUES (?, ?, ?)",
- url, feed.Title, time.Now().UTC().Format(time.RFC3339),
- )
- if err != nil {
- return nil, fmt.Errorf("failed to insert feed: %w", err)
- }
-
- id, err := result.LastInsertId()
- if err != nil {
- return nil, fmt.Errorf("failed to get last insert id: %w", err)
- }
-
- // Insert articles from the feed
- for _, item := range feed.Items {
- _, err = r.DB.Exec(
- "INSERT INTO articles (feed_id, guid, title, url, is_read) VALUES (?, ?, ?, ?, ?)",
- id, item.GUID, item.Title, item.Link, 0,
- )
- if err != nil {
- // Log but don't fail on individual article errors
- fmt.Printf("Failed to insert article: %v\n", err)
- }
- }
-
- return &model.Feed{
- ID: strconv.FormatInt(id, 10),
- URL: url,
- Title: feed.Title,
- FetchedAt: time.Now().Format(time.RFC3339),
- }, nil
-}
-
-// RemoveFeed is the resolver for the removeFeed field.
-func (r *mutationResolver) RemoveFeed(ctx context.Context, id string) (bool, error) {
- feedID, err := strconv.ParseInt(id, 10, 64)
- if err != nil {
- return false, fmt.Errorf("invalid feed ID: %w", err)
- }
-
- // Start a transaction
- tx, err := r.DB.Begin()
- if err != nil {
- return false, fmt.Errorf("failed to begin transaction: %w", err)
- }
- defer tx.Rollback()
-
- // Delete articles first (foreign key constraint)
- _, err = tx.Exec("DELETE FROM articles WHERE feed_id = ?", feedID)
- if err != nil {
- return false, fmt.Errorf("failed to delete articles: %w", err)
- }
-
- // Delete the feed
- result, err := tx.Exec("DELETE FROM feeds WHERE id = ?", feedID)
- if err != nil {
- return false, fmt.Errorf("failed to delete feed: %w", err)
- }
-
- rowsAffected, err := result.RowsAffected()
- if err != nil {
- return false, fmt.Errorf("failed to get rows affected: %w", err)
- }
-
- if rowsAffected == 0 {
- return false, fmt.Errorf("feed not found")
- }
-
- err = tx.Commit()
- if err != nil {
- return false, fmt.Errorf("failed to commit transaction: %w", err)
- }
-
- return true, nil
-}
-
-// MarkArticleRead is the resolver for the markArticleRead field.
-func (r *mutationResolver) MarkArticleRead(ctx context.Context, id string) (*model.Article, error) {
- articleID, err := strconv.ParseInt(id, 10, 64)
- if err != nil {
- return nil, fmt.Errorf("invalid article ID: %w", err)
- }
-
- // Update the article's read status
- _, err = r.DB.Exec("UPDATE articles SET is_read = 1 WHERE id = ?", articleID)
- if err != nil {
- return nil, fmt.Errorf("failed to mark article as read: %w", err)
- }
-
- // Fetch the updated article
- return r.Query().Article(ctx, id)
-}
-
-// MarkArticleUnread is the resolver for the markArticleUnread field.
-func (r *mutationResolver) MarkArticleUnread(ctx context.Context, id string) (*model.Article, error) {
- articleID, err := strconv.ParseInt(id, 10, 64)
- if err != nil {
- return nil, fmt.Errorf("invalid article ID: %w", err)
- }
-
- // Update the article's read status
- _, err = r.DB.Exec("UPDATE articles SET is_read = 0 WHERE id = ?", articleID)
- if err != nil {
- return nil, fmt.Errorf("failed to mark article as unread: %w", err)
- }
-
- // Fetch the updated article
- return r.Query().Article(ctx, id)
-}
-
-// MarkFeedRead is the resolver for the markFeedRead field.
-func (r *mutationResolver) MarkFeedRead(ctx context.Context, id string) (*model.Feed, error) {
- feedID, err := strconv.ParseInt(id, 10, 64)
- if err != nil {
- return nil, fmt.Errorf("invalid feed ID: %w", err)
- }
-
- // Update all articles in the feed to be read
- _, err = r.DB.Exec("UPDATE articles SET is_read = 1 WHERE feed_id = ?", feedID)
- if err != nil {
- return nil, fmt.Errorf("failed to mark feed as read: %w", err)
- }
-
- // Fetch the updated feed
- return r.Query().Feed(ctx, id)
-}
-
-// MarkFeedUnread is the resolver for the markFeedUnread field.
-func (r *mutationResolver) MarkFeedUnread(ctx context.Context, id string) (*model.Feed, error) {
- feedID, err := strconv.ParseInt(id, 10, 64)
- if err != nil {
- return nil, fmt.Errorf("invalid feed ID: %w", err)
- }
-
- // Update all articles in the feed to be unread
- _, err = r.DB.Exec("UPDATE articles SET is_read = 0 WHERE feed_id = ?", feedID)
- if err != nil {
- return nil, fmt.Errorf("failed to mark feed as unread: %w", err)
- }
-
- // Fetch the updated feed
- return r.Query().Feed(ctx, id)
-}
-
-// Feeds is the resolver for the feeds field.
-func (r *queryResolver) Feeds(ctx context.Context) ([]*model.Feed, error) {
- rows, err := r.DB.Query("SELECT id, url, title, fetched_at FROM feeds")
- if err != nil {
- return nil, fmt.Errorf("failed to query feeds: %w", err)
- }
- defer rows.Close()
-
- var feeds []*model.Feed
- for rows.Next() {
- var feed model.Feed
- err := rows.Scan(&feed.ID, &feed.URL, &feed.Title, &feed.FetchedAt)
- if err != nil {
- return nil, fmt.Errorf("failed to scan feed: %w", err)
- }
- feeds = append(feeds, &feed)
- }
-
- if err = rows.Err(); err != nil {
- return nil, fmt.Errorf("error iterating over feeds: %w", err)
- }
-
- return feeds, nil
-}
-
-// UnreadArticles is the resolver for the unreadArticles field.
-func (r *queryResolver) UnreadArticles(ctx context.Context) ([]*model.Article, error) {
- rows, err := r.DB.Query(`
- SELECT a.id, a.feed_id, a.guid, a.title, a.url, a.is_read,
- f.id, f.url, f.title
- FROM articles AS a
- INNER JOIN feeds AS f ON a.feed_id = f.id
- WHERE a.is_read = 0
- ORDER BY a.id DESC
- LIMIT 100
- `)
- if err != nil {
- return nil, fmt.Errorf("failed to query unread articles: %w", err)
- }
- defer rows.Close()
-
- var articles []*model.Article
- for rows.Next() {
- var article model.Article
- var feed model.Feed
- var isRead int
- err := rows.Scan(
- &article.ID, &article.FeedID, &article.GUID, &article.Title, &article.URL, &isRead,
- &feed.ID, &feed.URL, &feed.Title,
- )
- if err != nil {
- return nil, fmt.Errorf("failed to scan article: %w", err)
- }
- article.IsRead = isRead == 1
- article.Feed = &feed
- articles = append(articles, &article)
- }
-
- if err = rows.Err(); err != nil {
- return nil, fmt.Errorf("error iterating over articles: %w", err)
- }
-
- return articles, nil
-}
-
-// ReadArticles is the resolver for the readArticles field.
-func (r *queryResolver) ReadArticles(ctx context.Context) ([]*model.Article, error) {
- rows, err := r.DB.Query(`
- SELECT a.id, a.feed_id, a.guid, a.title, a.url, a.is_read,
- f.id, f.url, f.title
- FROM articles AS a
- INNER JOIN feeds AS f ON a.feed_id = f.id
- WHERE a.is_read = 1
- ORDER BY a.id DESC
- LIMIT 100
- `)
- if err != nil {
- return nil, fmt.Errorf("failed to query read articles: %w", err)
- }
- defer rows.Close()
-
- var articles []*model.Article
- for rows.Next() {
- var article model.Article
- var feed model.Feed
- var isRead int
- err := rows.Scan(
- &article.ID, &article.FeedID, &article.GUID, &article.Title, &article.URL, &isRead,
- &feed.ID, &feed.URL, &feed.Title,
- )
- if err != nil {
- return nil, fmt.Errorf("failed to scan article: %w", err)
- }
- article.IsRead = isRead == 1
- article.Feed = &feed
- articles = append(articles, &article)
- }
-
- if err = rows.Err(); err != nil {
- return nil, fmt.Errorf("error iterating over articles: %w", err)
- }
-
- return articles, nil
-}
-
-// Feed is the resolver for the feed field.
-func (r *queryResolver) Feed(ctx context.Context, id string) (*model.Feed, error) {
- feedID, err := strconv.ParseInt(id, 10, 64)
- if err != nil {
- return nil, fmt.Errorf("invalid feed ID: %w", err)
- }
-
- var feed model.Feed
- err = r.DB.QueryRow(
- "SELECT id, url, title, fetched_at FROM feeds WHERE id = ?",
- feedID,
- ).Scan(&feed.ID, &feed.URL, &feed.Title, &feed.FetchedAt)
- if err != nil {
- if err == sql.ErrNoRows {
- return nil, fmt.Errorf("feed not found")
- }
- return nil, fmt.Errorf("failed to query feed: %w", err)
- }
-
- return &feed, nil
-}
-
-// Article is the resolver for the article field.
-func (r *queryResolver) Article(ctx context.Context, id string) (*model.Article, error) {
- articleID, err := strconv.ParseInt(id, 10, 64)
- if err != nil {
- return nil, fmt.Errorf("invalid article ID: %w", err)
- }
-
- var article model.Article
- var feed model.Feed
- var isRead int
- err = r.DB.QueryRow(`
- SELECT a.id, a.feed_id, a.guid, a.title, a.url, a.is_read,
- f.id, f.url, f.title
- FROM articles AS a
- INNER JOIN feeds AS f ON a.feed_id = f.id
- WHERE a.id = ?
- `, articleID).Scan(
- &article.ID, &article.FeedID, &article.GUID, &article.Title, &article.URL, &isRead,
- &feed.ID, &feed.URL, &feed.Title,
- )
- if err != nil {
- if err == sql.ErrNoRows {
- return nil, fmt.Errorf("article not found")
- }
- return nil, fmt.Errorf("failed to query article: %w", err)
- }
- article.IsRead = isRead == 1
- article.Feed = &feed
-
- return &article, nil
-}
-
-// Mutation returns MutationResolver implementation.
-func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} }
-
-// Query returns QueryResolver implementation.
-func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }
-
-type mutationResolver struct{ *Resolver }
-type queryResolver struct{ *Resolver }