-
Notifications
You must be signed in to change notification settings - Fork 10
feat: unify feed preview targets #780
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,6 +35,7 @@ | |
| internalScheme = "feedcraft" | ||
| internalResourceTypeRecipe = "recipe" | ||
| internalResourceTypeTopic = "topic" | ||
| internalResourceTypeInbox = "inbox" | ||
| ) | ||
|
|
||
| // InputSpec is the unified runtime input model for RecipeFeed and TopicFeed. | ||
|
|
@@ -61,6 +62,10 @@ | |
| return NewBuilder(nil).BuildProviderFromInput(ctx, spec, stack) | ||
| } | ||
|
|
||
| func BuildProviderFromInputWithRecipeTrigger(ctx context.Context, spec InputSpec, stack []string, recipeTrigger string) (engine.FeedProvider, error) { | ||
| return NewBuilder(nil).BuildProviderFromInputWithRecipeTrigger(ctx, spec, stack, recipeTrigger) | ||
| } | ||
|
|
||
| func BuildTopicProvider(ctx context.Context, topicID string) (engine.FeedProvider, error) { | ||
| return NewBuilder(nil).BuildTopicProvider(ctx, topicID) | ||
| } | ||
|
|
@@ -82,9 +87,16 @@ | |
| } | ||
|
|
||
| func (b *Builder) BuildProviderFromInput(ctx context.Context, spec InputSpec, stack []string) (engine.FeedProvider, error) { | ||
| return b.BuildProviderFromInputWithRecipeTrigger(ctx, spec, stack, observability.TriggerTopicAggregation) | ||
| } | ||
|
|
||
| func (b *Builder) BuildProviderFromInputWithRecipeTrigger(ctx context.Context, spec InputSpec, stack []string, recipeTrigger string) (engine.FeedProvider, error) { | ||
| if strings.TrimSpace(recipeTrigger) == "" { | ||
| recipeTrigger = observability.TriggerTopicAggregation | ||
| } | ||
| switch spec.Kind { | ||
| case InputKindURI: | ||
| return b.buildProviderFromURI(ctx, spec.URI, stack) | ||
| return b.buildProviderFromURI(ctx, spec.URI, stack, recipeTrigger) | ||
| case InputKindSource: | ||
| if spec.SourceConfig == nil { | ||
| return nil, errors.New("source input requires source_config") | ||
|
|
@@ -212,7 +224,7 @@ | |
| }, nil | ||
| } | ||
|
|
||
| func (b *Builder) buildProviderFromURI(ctx context.Context, rawURI string, stack []string) (engine.FeedProvider, error) { | ||
| func (b *Builder) buildProviderFromURI(ctx context.Context, rawURI string, stack []string, recipeTrigger string) (engine.FeedProvider, error) { | ||
| if rawURI == "" { | ||
| return nil, errors.New("uri input requires a non-empty uri") | ||
| } | ||
|
|
@@ -232,9 +244,11 @@ | |
| } | ||
| switch resourceType { | ||
| case internalResourceTypeRecipe: | ||
| return b.buildRecipeProvider(ctx, resourceID, observability.TriggerTopicAggregation) | ||
| return b.buildRecipeProvider(ctx, resourceID, recipeTrigger) | ||
| case internalResourceTypeTopic: | ||
| return b.buildTopicProvider(ctx, resourceID, stack) | ||
| case internalResourceTypeInbox: | ||
| return &InboxProvider{DB: b.db(), InboxID: resourceID}, nil | ||
| default: | ||
| return nil, fmt.Errorf("unsupported internal resource type %q", resourceType) | ||
| } | ||
|
|
@@ -438,6 +452,56 @@ | |
| return resourceType, resourceID, nil | ||
| } | ||
|
|
||
| // InboxProvider adapts inbox items into a feed without requiring an intermediate recipe. | ||
| type InboxProvider struct { | ||
| DB *gorm.DB | ||
| InboxID string | ||
| } | ||
|
|
||
| func (p *InboxProvider) Fetch(ctx context.Context) (*model.CraftFeed, error) { | ||
| _ = ctx | ||
| db := p.DB | ||
| if db == nil { | ||
| db = util.GetDatabase() | ||
| } | ||
|
|
||
| inbox, err := dao.GetInboxByID(db, p.InboxID) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to get inbox %s: %w", p.InboxID, err) | ||
| } | ||
|
|
||
| items, err := dao.ListInboxItems(db, p.InboxID) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to list inbox items: %w", err) | ||
| } | ||
|
|
||
| feed := &model.CraftFeed{ | ||
| Title: inbox.Title, | ||
| Description: inbox.Description, | ||
| Id: fmt.Sprintf("inbox:%s", p.InboxID), | ||
| Link: p.BaseURL(), | ||
| Articles: make([]*model.CraftArticle, 0, len(items)), | ||
| } | ||
| for _, item := range items { | ||
| feed.Articles = append(feed.Articles, &model.CraftArticle{ | ||
| Title: item.Title, | ||
| Link: item.URL, | ||
| Content: item.Content, | ||
| Description: item.Summary, | ||
| Id: item.ItemID, | ||
| AuthorName: item.Author, | ||
| Created: item.PublishedAt, | ||
| Updated: item.PublishedAt, | ||
| }) | ||
| } | ||
|
|
||
| return feed, nil | ||
| } | ||
|
|
||
| func (p *InboxProvider) BaseURL() string { | ||
| return fmt.Sprintf("feedcraft://inbox/%s", p.InboxID) | ||
|
Comment on lines
+501
to
+502
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nitpick: Reuse the internal inbox resource type constant when constructing the feedcraft URI
|
||
| } | ||
|
|
||
| // RecipeProvider adapts a runtime RecipeFeed and adds execution metadata. | ||
| type RecipeProvider struct { | ||
| Recipe *engine.RecipeFeed | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
ctxparameter is currently ignored inInboxProvider.Fetch. It is best practice to usedb.WithContext(ctx)to ensure that database operations respect the request context, allowing for proper timeout and cancellation handling.