auto-update-daily-20260202
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}