diff options
| author | nsfisis <nsfisis@gmail.com> | 2025-07-12 17:11:13 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2025-07-12 17:52:54 +0900 |
| commit | fbe4bff7e8b6a5239c490601436fb3638dc8e13b (patch) | |
| tree | b011c43d20ebfc4566cdbe95ed878c9644797e37 /backend/graphql/resolver/schema.resolvers.go | |
| parent | db4f7f4ee12ab52ff249b29496a9f0997e3dbbf5 (diff) | |
| download | feedaka-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.go | 309 |
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 } |
