From 816b9300ed904524e8bb6de6b68619b070706aae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Sibiril?= <81782+aureliensibiril@users.noreply.github.com> Date: Thu, 9 Apr 2026 01:05:26 +0200 Subject: [PATCH] fix(slack): select widest-scope connector in sender MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sendMessage and updateMessage used LoadAllByOrganizationIDProtocol AndProvider and then picked connectors[0], which is ordered by created_at ASC. On an organization with multiple Slack installs only the oldest install ever received messages — re-installing Slack to switch channels silently broke because the sort kept the old row winning. Switch to LoadOneByOrganizationIDAndProvider, which picks the widest-scope row with updated_at DESC as the tiebreak. For Slack the scope sets are typically identical across installs, so the effective behavior becomes "most recent install wins" — the expected behavior. While here, prefix the remaining error messages in this file with "cannot" to match the project convention. Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com> --- pkg/slack/sender.go | 54 ++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/pkg/slack/sender.go b/pkg/slack/sender.go index f3c1d9c83..4a9c5eb3f 100644 --- a/pkg/slack/sender.go +++ b/pkg/slack/sender.go @@ -148,39 +148,36 @@ func (s *Sender) sendMessage(ctx context.Context, tx pg.Querier, message *coreda tenantID := message.ID.TenantID() scope := coredata.NewScope(tenantID) - var connectors coredata.Connectors - if err := connectors.LoadAllByOrganizationIDProtocolAndProvider( + var c coredata.Connector + if err := c.LoadOneByOrganizationIDAndProvider( ctx, tx, scope, - message.OrganizationID, - coredata.ConnectorProtocolOAuth2, - coredata.ConnectorProviderSlack, s.encryptionKey, + message.OrganizationID, + coredata.ConnectorProviderSlack, ); err != nil { - return nil, nil, fmt.Errorf("cannot load slack connectors: %w", err) + if errors.Is(err, coredata.ErrResourceNotFound) { + return nil, nil, fmt.Errorf("cannot send slack message: no connector configured for organization") + } + return nil, nil, fmt.Errorf("cannot load slack connector: %w", err) } - if len(connectors) == 0 { - return nil, nil, fmt.Errorf("no slack connectors configured for organization") - } - - c := connectors[0] if c.Connection == nil { - return nil, nil, fmt.Errorf("slack connector has nil connection") + return nil, nil, fmt.Errorf("cannot send slack message: connector has nil connection") } slackConn, ok := c.Connection.(*connector.SlackConnection) if !ok { - return nil, nil, fmt.Errorf("slack connector must have SlackConnection type, got %T", c.Connection) + return nil, nil, fmt.Errorf("cannot send slack message: unexpected connection type %T", c.Connection) } if slackConn.Settings.ChannelID == "" { - return nil, nil, fmt.Errorf("slack connector %s has no channel ID", c.ID) + return nil, nil, fmt.Errorf("cannot send slack message: connector %s has no channel ID", c.ID) } if slackConn.AccessToken == "" { - return nil, nil, fmt.Errorf("slack connector %s has no access token", c.ID) + return nil, nil, fmt.Errorf("cannot send slack message: connector %s has no access token", c.ID) } client := NewClient(s.logger) @@ -269,41 +266,38 @@ func (s *Sender) batchUpdateMessages(ctx context.Context) error { func (s *Sender) updateMessage(ctx context.Context, tx pg.Querier, updateMessage *coredata.SlackMessage) error { if updateMessage.ChannelID == nil || updateMessage.MessageTS == nil { - return fmt.Errorf("slack message has no channel ID or message TS") + return fmt.Errorf("cannot update slack message: missing channel ID or message TS") } tenantID := updateMessage.ID.TenantID() scope := coredata.NewScope(tenantID) - var connectors coredata.Connectors - if err := connectors.LoadAllByOrganizationIDProtocolAndProvider( + var c coredata.Connector + if err := c.LoadOneByOrganizationIDAndProvider( ctx, tx, scope, - updateMessage.OrganizationID, - coredata.ConnectorProtocolOAuth2, - coredata.ConnectorProviderSlack, s.encryptionKey, + updateMessage.OrganizationID, + coredata.ConnectorProviderSlack, ); err != nil { - return fmt.Errorf("cannot load slack connectors: %w", err) + if errors.Is(err, coredata.ErrResourceNotFound) { + return fmt.Errorf("cannot update slack message: no connector configured for organization") + } + return fmt.Errorf("cannot load slack connector: %w", err) } - if len(connectors) == 0 { - return fmt.Errorf("no slack connectors configured for organization") - } - - c := connectors[0] if c.Connection == nil { - return fmt.Errorf("slack connector has nil connection") + return fmt.Errorf("cannot update slack message: connector has nil connection") } slackConn, ok := c.Connection.(*connector.SlackConnection) if !ok { - return fmt.Errorf("slack connector must have SlackConnection type, got %T", c.Connection) + return fmt.Errorf("cannot update slack message: unexpected connection type %T", c.Connection) } if slackConn.AccessToken == "" { - return fmt.Errorf("slack connector %s has no access token", c.ID) + return fmt.Errorf("cannot update slack message: connector %s has no access token", c.ID) } client := NewClient(s.logger)