diff options
Diffstat (limited to 'backend/graphql')
| -rw-r--r-- | backend/graphql/resolver/resolver.go | 15 | ||||
| -rw-r--r-- | backend/graphql/resolver/schema.resolvers.go (renamed from backend/graphql/resolvers.go) | 236 |
2 files changed, 117 insertions, 134 deletions
diff --git a/backend/graphql/resolver/resolver.go b/backend/graphql/resolver/resolver.go new file mode 100644 index 0000000..7a9c389 --- /dev/null +++ b/backend/graphql/resolver/resolver.go @@ -0,0 +1,15 @@ +package resolver + +import ( + "database/sql" + "undef.ninja/x/feedaka/db" +) + +// This file will not be regenerated automatically. +// +// It serves as dependency injection for your app, add any dependencies you require here. + +type Resolver struct { + DB *sql.DB + Queries *db.Queries +} diff --git a/backend/graphql/resolvers.go b/backend/graphql/resolver/schema.resolvers.go index 3e73162..0ee771b 100644 --- a/backend/graphql/resolvers.go +++ b/backend/graphql/resolver/schema.resolvers.go @@ -1,6 +1,8 @@ -package graphql +package resolver -// 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. +// This file will be automatically regenerated based on the schema, any resolver implementations +// will be copied through when generating and any unknown code will be moved to the end. +// Code generated by github.com/99designs/gqlgen version v0.17.76 import ( "context" @@ -10,14 +12,11 @@ import ( "time" "github.com/mmcdole/gofeed" - + "undef.ninja/x/feedaka/db" + gql "undef.ninja/x/feedaka/graphql" "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 @@ -28,25 +27,24 @@ func (r *mutationResolver) AddFeed(ctx context.Context, url string) (*model.Feed } // 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), - ) + dbFeed, err := r.Queries.CreateFeed(ctx, db.CreateFeedParams{ + Url: url, + Title: feed.Title, + FetchedAt: 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, - ) + _, err = r.Queries.CreateArticle(ctx, db.CreateArticleParams{ + FeedID: dbFeed.ID, + Guid: item.GUID, + Title: item.Title, + Url: item.Link, + IsRead: 0, + }) if err != nil { // Log but don't fail on individual article errors fmt.Printf("Failed to insert article: %v\n", err) @@ -54,10 +52,10 @@ func (r *mutationResolver) AddFeed(ctx context.Context, url string) (*model.Feed } return &model.Feed{ - ID: strconv.FormatInt(id, 10), - URL: url, - Title: feed.Title, - FetchedAt: time.Now().Format(time.RFC3339), + ID: strconv.FormatInt(dbFeed.ID, 10), + URL: dbFeed.Url, + Title: dbFeed.Title, + FetchedAt: dbFeed.FetchedAt, }, nil } @@ -75,27 +73,23 @@ func (r *mutationResolver) RemoveFeed(ctx context.Context, id string) (bool, err } defer tx.Rollback() + qtx := r.Queries.WithTx(tx) + // Delete articles first (foreign key constraint) - _, err = tx.Exec("DELETE FROM articles WHERE feed_id = ?", feedID) + err = qtx.DeleteArticlesByFeed(ctx, 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) + err = qtx.DeleteFeed(ctx, feedID) if err != nil { + if err == sql.ErrNoRows { + return false, fmt.Errorf("feed not found") + } 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) @@ -112,7 +106,10 @@ func (r *mutationResolver) MarkArticleRead(ctx context.Context, id string) (*mod } // Update the article's read status - _, err = r.DB.Exec("UPDATE articles SET is_read = 1 WHERE id = ?", articleID) + err = r.Queries.UpdateArticleReadStatus(ctx, db.UpdateArticleReadStatusParams{ + IsRead: 1, + ID: articleID, + }) if err != nil { return nil, fmt.Errorf("failed to mark article as read: %w", err) } @@ -129,7 +126,10 @@ func (r *mutationResolver) MarkArticleUnread(ctx context.Context, id string) (*m } // Update the article's read status - _, err = r.DB.Exec("UPDATE articles SET is_read = 0 WHERE id = ?", articleID) + err = r.Queries.UpdateArticleReadStatus(ctx, db.UpdateArticleReadStatusParams{ + IsRead: 0, + ID: articleID, + }) if err != nil { return nil, fmt.Errorf("failed to mark article as unread: %w", err) } @@ -146,7 +146,7 @@ func (r *mutationResolver) MarkFeedRead(ctx context.Context, id string) (*model. } // Update all articles in the feed to be read - _, err = r.DB.Exec("UPDATE articles SET is_read = 1 WHERE feed_id = ?", feedID) + err = r.Queries.MarkFeedArticlesRead(ctx, feedID) if err != nil { return nil, fmt.Errorf("failed to mark feed as read: %w", err) } @@ -163,7 +163,7 @@ func (r *mutationResolver) MarkFeedUnread(ctx context.Context, id string) (*mode } // Update all articles in the feed to be unread - _, err = r.DB.Exec("UPDATE articles SET is_read = 0 WHERE feed_id = ?", feedID) + err = r.Queries.MarkFeedArticlesUnread(ctx, feedID) if err != nil { return nil, fmt.Errorf("failed to mark feed as unread: %w", err) } @@ -174,24 +174,19 @@ func (r *mutationResolver) MarkFeedUnread(ctx context.Context, id string) (*mode // 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") + dbFeeds, err := r.Queries.GetFeeds(ctx) 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) + for _, dbFeed := range dbFeeds { + feeds = append(feeds, &model.Feed{ + ID: strconv.FormatInt(dbFeed.ID, 10), + URL: dbFeed.Url, + Title: dbFeed.Title, + FetchedAt: dbFeed.FetchedAt, + }) } return feeds, nil @@ -199,39 +194,26 @@ func (r *queryResolver) Feeds(ctx context.Context) ([]*model.Feed, error) { // 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 - `) + rows, err := r.Queries.GetUnreadArticles(ctx) 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) + for _, row := range rows { + articles = append(articles, &model.Article{ + ID: strconv.FormatInt(row.ID, 10), + FeedID: strconv.FormatInt(row.FeedID, 10), + GUID: row.Guid, + Title: row.Title, + URL: row.Url, + IsRead: row.IsRead == 1, + Feed: &model.Feed{ + ID: strconv.FormatInt(row.FeedID2, 10), + URL: row.FeedUrl, + Title: row.FeedTitle, + }, + }) } return articles, nil @@ -239,39 +221,26 @@ func (r *queryResolver) UnreadArticles(ctx context.Context) ([]*model.Article, e // 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 - `) + rows, err := r.Queries.GetReadArticles(ctx) 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) + for _, row := range rows { + articles = append(articles, &model.Article{ + ID: strconv.FormatInt(row.ID, 10), + FeedID: strconv.FormatInt(row.FeedID, 10), + GUID: row.Guid, + Title: row.Title, + URL: row.Url, + IsRead: row.IsRead == 1, + Feed: &model.Feed{ + ID: strconv.FormatInt(row.FeedID2, 10), + URL: row.FeedUrl, + Title: row.FeedTitle, + }, + }) } return articles, nil @@ -284,11 +253,7 @@ func (r *queryResolver) Feed(ctx context.Context, id string) (*model.Feed, error 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) + dbFeed, err := r.Queries.GetFeed(ctx, feedID) if err != nil { if err == sql.ErrNoRows { return nil, fmt.Errorf("feed not found") @@ -296,7 +261,12 @@ func (r *queryResolver) Feed(ctx context.Context, id string) (*model.Feed, error return nil, fmt.Errorf("failed to query feed: %w", err) } - return &feed, nil + return &model.Feed{ + ID: strconv.FormatInt(dbFeed.ID, 10), + URL: dbFeed.Url, + Title: dbFeed.Title, + FetchedAt: dbFeed.FetchedAt, + }, nil } // Article is the resolver for the article field. @@ -306,36 +276,34 @@ func (r *queryResolver) Article(ctx context.Context, id string) (*model.Article, 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, - ) + row, err := r.Queries.GetArticle(ctx, articleID) 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 + return &model.Article{ + ID: strconv.FormatInt(row.ID, 10), + FeedID: strconv.FormatInt(row.FeedID, 10), + GUID: row.Guid, + Title: row.Title, + URL: row.Url, + IsRead: row.IsRead == 1, + Feed: &model.Feed{ + ID: strconv.FormatInt(row.FeedID2, 10), + URL: row.FeedUrl, + Title: row.FeedTitle, + }, + }, nil } -// Mutation returns MutationResolver implementation. -func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} } +// Mutation returns gql.MutationResolver implementation. +func (r *Resolver) Mutation() gql.MutationResolver { return &mutationResolver{r} } -// Query returns QueryResolver implementation. -func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } +// Query returns gql.QueryResolver implementation. +func (r *Resolver) Query() gql.QueryResolver { return &queryResolver{r} } type mutationResolver struct{ *Resolver } type queryResolver struct{ *Resolver } |
