aboutsummaryrefslogtreecommitdiffhomepage
path: root/backend/graphql/resolver/schema.resolvers.go
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-07-12 17:11:13 +0900
committernsfisis <nsfisis@gmail.com>2025-07-12 17:52:54 +0900
commitfbe4bff7e8b6a5239c490601436fb3638dc8e13b (patch)
treeb011c43d20ebfc4566cdbe95ed878c9644797e37 /backend/graphql/resolver/schema.resolvers.go
parentdb4f7f4ee12ab52ff249b29496a9f0997e3dbbf5 (diff)
downloadfeedaka-fbe4bff7e8b6a5239c490601436fb3638dc8e13b.tar.gz
feedaka-fbe4bff7e8b6a5239c490601436fb3638dc8e13b.tar.zst
feedaka-fbe4bff7e8b6a5239c490601436fb3638dc8e13b.zip
feat(backend): introduce sqlc
Diffstat (limited to 'backend/graphql/resolver/schema.resolvers.go')
-rw-r--r--backend/graphql/resolver/schema.resolvers.go309
1 files changed, 309 insertions, 0 deletions
diff --git a/backend/graphql/resolver/schema.resolvers.go b/backend/graphql/resolver/schema.resolvers.go
new file mode 100644
index 0000000..0ee771b
--- /dev/null
+++ b/backend/graphql/resolver/schema.resolvers.go
@@ -0,0 +1,309 @@
+package resolver
+
+// 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"
+ "database/sql"
+ "fmt"
+ "strconv"
+ "time"
+
+ "github.com/mmcdole/gofeed"
+ "undef.ninja/x/feedaka/db"
+ gql "undef.ninja/x/feedaka/graphql"
+ "undef.ninja/x/feedaka/graphql/model"
+)
+
+// 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
+ 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)
+ }
+
+ // Insert articles from the feed
+ for _, item := range feed.Items {
+ _, 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)
+ }
+ }
+
+ return &model.Feed{
+ ID: strconv.FormatInt(dbFeed.ID, 10),
+ URL: dbFeed.Url,
+ Title: dbFeed.Title,
+ FetchedAt: dbFeed.FetchedAt,
+ }, 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()
+
+ qtx := r.Queries.WithTx(tx)
+
+ // Delete articles first (foreign key constraint)
+ err = qtx.DeleteArticlesByFeed(ctx, feedID)
+ if err != nil {
+ return false, fmt.Errorf("failed to delete articles: %w", err)
+ }
+
+ // Delete the feed
+ 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)
+ }
+
+ 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.Queries.UpdateArticleReadStatus(ctx, db.UpdateArticleReadStatusParams{
+ IsRead: 1,
+ 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.Queries.UpdateArticleReadStatus(ctx, db.UpdateArticleReadStatusParams{
+ IsRead: 0,
+ 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.Queries.MarkFeedArticlesRead(ctx, 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.Queries.MarkFeedArticlesUnread(ctx, 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) {
+ dbFeeds, err := r.Queries.GetFeeds(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("failed to query feeds: %w", err)
+ }
+
+ var feeds []*model.Feed
+ 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
+}
+
+// UnreadArticles is the resolver for the unreadArticles field.
+func (r *queryResolver) UnreadArticles(ctx context.Context) ([]*model.Article, error) {
+ rows, err := r.Queries.GetUnreadArticles(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("failed to query unread articles: %w", err)
+ }
+
+ var articles []*model.Article
+ 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
+}
+
+// ReadArticles is the resolver for the readArticles field.
+func (r *queryResolver) ReadArticles(ctx context.Context) ([]*model.Article, error) {
+ rows, err := r.Queries.GetReadArticles(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("failed to query read articles: %w", err)
+ }
+
+ var articles []*model.Article
+ 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
+}
+
+// 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)
+ }
+
+ dbFeed, err := r.Queries.GetFeed(ctx, feedID)
+ 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 &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.
+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)
+ }
+
+ 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)
+ }
+
+ 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 gql.MutationResolver implementation.
+func (r *Resolver) Mutation() gql.MutationResolver { return &mutationResolver{r} }
+
+// Query returns gql.QueryResolver implementation.
+func (r *Resolver) Query() gql.QueryResolver { return &queryResolver{r} }
+
+type mutationResolver struct{ *Resolver }
+type queryResolver struct{ *Resolver }