-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathhome_handler.go
More file actions
193 lines (162 loc) · 5.81 KB
/
home_handler.go
File metadata and controls
193 lines (162 loc) · 5.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
package slack
import (
"context"
"errors"
"fmt"
"log/slog"
"strings"
"github.com/codeGROOVE-dev/slacker/pkg/config"
"github.com/codeGROOVE-dev/slacker/pkg/github"
"github.com/codeGROOVE-dev/slacker/pkg/home"
"github.com/codeGROOVE-dev/slacker/pkg/state"
"github.com/codeGROOVE-dev/slacker/pkg/usermapping"
gogithub "github.com/google/go-github/v50/github"
)
// HomeHandler handles app_home_opened events for a workspace.
type HomeHandler struct {
slackManager *Manager
githubManager *github.Manager
configManager *config.Manager
stateStore state.Store
reverseMapping *usermapping.ReverseService
}
// NewHomeHandler creates a new home view handler.
func NewHomeHandler(
slackManager *Manager,
githubManager *github.Manager,
configManager *config.Manager,
stateStore state.Store,
reverseMapping *usermapping.ReverseService,
) *HomeHandler {
return &HomeHandler{
slackManager: slackManager,
githubManager: githubManager,
configManager: configManager,
stateStore: stateStore,
reverseMapping: reverseMapping,
}
}
// HandleAppHomeOpened updates the app home view when a user opens it.
func (h *HomeHandler) HandleAppHomeOpened(ctx context.Context, teamID, slackUserID string) error {
slog.Info("handling app home opened - fetching fresh data",
"team_id", teamID,
"slack_user_id", slackUserID)
// Try up to 2 times - first with cached client, second with fresh client after invalid_auth
for attempt := range 2 {
if attempt > 0 {
slog.Info("retrying home view after invalid_auth", "team_id", teamID, "attempt", attempt+1)
}
err := h.tryHandleAppHomeOpened(ctx, teamID, slackUserID)
if err == nil {
return nil
}
// If invalid_auth and first attempt, invalidate cache and retry
if strings.Contains(err.Error(), "invalid_auth") && attempt == 0 {
slog.Warn("invalid_auth detected - invalidating cache and retrying",
"team_id", teamID)
h.slackManager.InvalidateCache(teamID)
continue
}
// Other errors or second attempt - return immediately
return err
}
return errors.New("failed after retries")
}
// tryHandleAppHomeOpened attempts to handle app home opened event.
func (h *HomeHandler) tryHandleAppHomeOpened(ctx context.Context, teamID, slackUserID string) error {
// Get Slack client for this workspace
slackClient, err := h.slackManager.Client(ctx, teamID)
if err != nil {
return fmt.Errorf("failed to get Slack client: %w", err)
}
// Get all orgs for this workspace
workspaceOrgs := h.workspaceOrgs(teamID)
if len(workspaceOrgs) == 0 {
slog.Warn("no workspace orgs found", "team_id", teamID)
return h.publishPlaceholderHome(ctx, slackClient, slackUserID, nil)
}
// Get config for first org to extract domain and user overrides
cfg, exists := h.configManager.Config(workspaceOrgs[0])
if !exists {
return fmt.Errorf("no config for org: %s", workspaceOrgs[0])
}
// Update reverse mapping overrides from config
if len(cfg.Users) > 0 {
h.reverseMapping.SetOverrides(cfg.Users)
}
// Map Slack user to GitHub username
mapping, err := h.reverseMapping.LookupGitHub(ctx, slackClient.API(), slackUserID, workspaceOrgs[0], cfg.Global.EmailDomain)
if err != nil {
slog.Warn("failed to map Slack user to GitHub",
"slack_user_id", slackUserID,
"error", err)
return h.publishPlaceholderHome(ctx, slackClient, slackUserID, nil)
}
githubUsername := mapping.GitHubUsername
// Get GitHub client for first org (they all share the same app)
githubClient, ok := h.githubManager.ClientForOrg(workspaceOrgs[0])
if !ok {
return fmt.Errorf("no GitHub client for org: %s", workspaceOrgs[0])
}
// Create fetcher and fetch dashboard
ghClient, ok := githubClient.Client().(*gogithub.Client)
if !ok {
return errors.New("failed to get GitHub client")
}
fetcher := home.NewFetcher(
ghClient,
h.stateStore,
githubClient.InstallationToken(ctx),
"ready-to-review[bot]",
)
dashboard, err := fetcher.FetchDashboard(ctx, githubUsername, workspaceOrgs)
if err != nil {
slog.Error("failed to fetch dashboard",
"github_user", githubUsername,
"error", err)
return h.publishPlaceholderHome(ctx, slackClient, slackUserID, mapping)
}
// Add workspace orgs to dashboard for UI display
dashboard.WorkspaceOrgs = workspaceOrgs
// Build Block Kit UI - use first org as primary, include debug info
blocks := home.BuildBlocksWithDebug(dashboard, workspaceOrgs[0], mapping)
// Publish to Slack
if err := slackClient.PublishHomeView(ctx, slackUserID, blocks); err != nil {
return fmt.Errorf("failed to publish home view: %w", err)
}
slog.Info("published home view with fresh data",
"slack_user_id", slackUserID,
"github_user", githubUsername,
"incoming_prs", len(dashboard.IncomingPRs),
"incoming_blocked", dashboard.Counts().IncomingBlocked,
"outgoing_prs", len(dashboard.OutgoingPRs),
"outgoing_blocked", dashboard.Counts().OutgoingBlocked,
"workspace_orgs", len(workspaceOrgs))
return nil
}
// workspaceOrgs returns all GitHub orgs configured for this Slack workspace.
func (h *HomeHandler) workspaceOrgs(teamID string) []string {
allOrgs := h.githubManager.AllOrgs()
var workspaceOrgs []string
for _, org := range allOrgs {
cfg, exists := h.configManager.Config(org)
if !exists {
continue
}
// Check if this org is configured for this workspace
if cfg.Global.TeamID == teamID {
workspaceOrgs = append(workspaceOrgs, org)
}
}
return workspaceOrgs
}
// publishPlaceholderHome publishes a simple placeholder home view.
func (*HomeHandler) publishPlaceholderHome(ctx context.Context, slackClient *Client, slackUserID string, mapping *usermapping.ReverseMapping) error {
slog.Debug("publishing placeholder home", "user_id", slackUserID)
blocks := home.BuildBlocksWithDebug(&home.Dashboard{
IncomingPRs: nil,
OutgoingPRs: nil,
WorkspaceOrgs: []string{"your-org"},
}, "your-org", mapping)
return slackClient.PublishHomeView(ctx, slackUserID, blocks)
}