flake-update-20260201
  1// Package sources provides data source implementations.
  2package sources
  3
  4import (
  5	"context"
  6	"sync"
  7	"time"
  8
  9	"github.com/vdemeester/home/tools/review-tool/internal/activity"
 10	"github.com/vdemeester/home/tools/review-tool/internal/config"
 11)
 12
 13// Source defines the interface for activity data sources.
 14type Source interface {
 15	// Name returns the source identifier.
 16	Name() string
 17
 18	// Fetch retrieves activities within the given time range.
 19	Fetch(ctx context.Context, start, end time.Time) (*activity.Activity, error)
 20
 21	// Validate checks if the source is properly configured.
 22	Validate() error
 23}
 24
 25// Registry holds all available sources.
 26type Registry struct {
 27	sources map[string]Source
 28}
 29
 30// NewRegistry creates a registry with sources based on configuration.
 31func NewRegistry(cfg *config.Config) *Registry {
 32	r := &Registry{sources: make(map[string]Source)}
 33
 34	if cfg.GitHub.Enabled {
 35		r.sources["github"] = NewGitHubSource(&cfg.GitHub)
 36	}
 37	if cfg.Org.Enabled {
 38		r.sources["org"] = NewOrgSource(&cfg.Org)
 39	}
 40	if cfg.Jira.Enabled {
 41		r.sources["jira"] = NewJiraSource(&cfg.Jira)
 42	}
 43	if cfg.Claude.Enabled {
 44		r.sources["claude"] = NewClaudeSource(&cfg.Claude)
 45	}
 46
 47	return r
 48}
 49
 50// Sources returns the list of registered source names.
 51func (r *Registry) Sources() []string {
 52	names := make([]string, 0, len(r.sources))
 53	for name := range r.sources {
 54		names = append(names, name)
 55	}
 56	return names
 57}
 58
 59// Get returns a source by name.
 60func (r *Registry) Get(name string) (Source, bool) {
 61	s, ok := r.sources[name]
 62	return s, ok
 63}
 64
 65// FetchAll runs all sources concurrently and returns results.
 66func (r *Registry) FetchAll(ctx context.Context, start, end time.Time) map[string]*activity.Activity {
 67	results := make(map[string]*activity.Activity)
 68	var mu sync.Mutex
 69	var wg sync.WaitGroup
 70
 71	for name, source := range r.sources {
 72		wg.Add(1)
 73		go func(name string, source Source) {
 74			defer wg.Done()
 75
 76			act, err := source.Fetch(ctx, start, end)
 77			if err != nil {
 78				act = &activity.Activity{
 79					Source: name,
 80					Error:  err.Error(),
 81				}
 82			}
 83
 84			mu.Lock()
 85			results[name] = act
 86			mu.Unlock()
 87		}(name, source)
 88	}
 89
 90	wg.Wait()
 91	return results
 92}
 93
 94// FetchSelected runs only the specified sources.
 95func (r *Registry) FetchSelected(ctx context.Context, start, end time.Time, names []string) map[string]*activity.Activity {
 96	results := make(map[string]*activity.Activity)
 97	var mu sync.Mutex
 98	var wg sync.WaitGroup
 99
100	for _, name := range names {
101		source, ok := r.sources[name]
102		if !ok {
103			continue
104		}
105
106		wg.Add(1)
107		go func(name string, source Source) {
108			defer wg.Done()
109
110			act, err := source.Fetch(ctx, start, end)
111			if err != nil {
112				act = &activity.Activity{
113					Source: name,
114					Error:  err.Error(),
115				}
116			}
117
118			mu.Lock()
119			results[name] = act
120			mu.Unlock()
121		}(name, source)
122	}
123
124	wg.Wait()
125	return results
126}