fix(js): scope default connectionIdentifier to subscriberId to prevent 409 conflicts#10896
Conversation
…t 409 conflicts
When SlackConnectButton, MsTeamsConnectButton, SlackLinkUser, or MsTeamsLinkUser
are rendered without an explicit connectionIdentifier, all subscribers in the same
environment shared the same hardcoded default (chconn-slack-default /
chconn-msteams-default). Because the channel_connections collection has a unique
index on { _environmentId, identifier }, the second subscriber to connect would
receive a 409 Conflict and be unable to connect.
Fix: derive the default identifier as `<prefix>-<subscriberId>` using the new
buildDefaultConnectionIdentifier helper so each subscriber gets their own unique
default. Falls back to the bare prefix when subscriberId is unavailable (shared
mode), matching existing behaviour for that edge case.
Made-with: Cursor
✅ Deploy Preview for dashboard-v2-novu-staging canceled.
|
📝 WalkthroughWalkthroughA new Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Hey there and thank you for opening this pull request! 👋 We require pull request titles to follow specific formatting rules and it looks like your proposed title needs to be adjusted. Your PR title is: Requirements:
Expected format: Details: PR title must end with 'fixes TICKET-ID' (e.g., 'fixes NOV-123') or include ticket ID in branch name |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/js/src/ui/components/slack-link-user/SlackLinkUser.tsx (1)
35-44:⚠️ Potential issue | 🟡 MinorRemove unused
subscriberIdparameter from hook initialization.The
subscriberIdpassed touseChannelEndpointon line 43 is not used by the hook—createResourceonly depends onendpointIdentifier. The actualgenerateLinkUserOAuthUrlcall correctly receives the resolvedsubscriberIdat invocation time (line 155), and list operations correctly use the derivedconnectionIdentifier. Passingprops.subscriberIdto the hook is misleading and should be removed.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/js/src/ui/components/slack-link-user/SlackLinkUser.tsx` around lines 35 - 44, The hook initialization passes an unused subscriberId prop; remove subscriberId: props.subscriberId from the useChannelEndpoint call and keep the rest (integrationIdentifier: integrationIdentifier(), connectionIdentifier: connectionIdentifier()) so generateLinkUserOAuthUrl continues to receive the resolved subscriber via its invocation; update the call site that constructs the hook (useChannelEndpoint) and ensure resolvedSubscriberId(), connectionIdentifier(), buildDefaultConnectionIdentifier, DEFAULT_SLACK_CONNECTION_IDENTIFIER, and generateLinkUserOAuthUrl remain unchanged.packages/js/src/ui/components/msteams-connect-button/MsTeamsConnectButton.tsx (1)
47-59:⚠️ Potential issue | 🟡 MinorPass the resolved subscriber ID to the hook for consistency.
The hook receives
subscriberId: props.subscriberIdbut derivesconnectionIdentifierfromresolvedSubscriberId(). While the hook only usesconnectionIdentifierfor its API calls and event listeners (notsubscriberId), the mismatched values are confusing. PassresolvedSubscriberId()instead to keep the component's state coherent.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/js/src/ui/components/msteams-connect-button/MsTeamsConnectButton.tsx` around lines 47 - 59, The component passes props.subscriberId into useChannelConnection while connectionIdentifier is built from resolvedSubscriberId(), causing inconsistent state; change the hook call to pass resolvedSubscriberId() instead of props.subscriberId so useChannelConnection receives the same subscriber id used by connectionIdentifier (update the useChannelConnection invocation that destructures connection, loading, disconnect, mutate, generateConnectOAuthUrl to use resolvedSubscriberId()).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In
`@packages/js/src/ui/components/msteams-connect-button/MsTeamsConnectButton.tsx`:
- Around line 47-59: The component passes props.subscriberId into
useChannelConnection while connectionIdentifier is built from
resolvedSubscriberId(), causing inconsistent state; change the hook call to pass
resolvedSubscriberId() instead of props.subscriberId so useChannelConnection
receives the same subscriber id used by connectionIdentifier (update the
useChannelConnection invocation that destructures connection, loading,
disconnect, mutate, generateConnectOAuthUrl to use resolvedSubscriberId()).
In `@packages/js/src/ui/components/slack-link-user/SlackLinkUser.tsx`:
- Around line 35-44: The hook initialization passes an unused subscriberId prop;
remove subscriberId: props.subscriberId from the useChannelEndpoint call and
keep the rest (integrationIdentifier: integrationIdentifier(),
connectionIdentifier: connectionIdentifier()) so generateLinkUserOAuthUrl
continues to receive the resolved subscriber via its invocation; update the call
site that constructs the hook (useChannelEndpoint) and ensure
resolvedSubscriberId(), connectionIdentifier(),
buildDefaultConnectionIdentifier, DEFAULT_SLACK_CONNECTION_IDENTIFIER, and
generateLinkUserOAuthUrl remain unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: c07e3ce4-1b42-4b26-b268-1f884bbb8567
📒 Files selected for processing (5)
packages/js/src/ui/components/constants.tspackages/js/src/ui/components/msteams-connect-button/MsTeamsConnectButton.tsxpackages/js/src/ui/components/msteams-link-user/MsTeamsLinkUser.tsxpackages/js/src/ui/components/slack-connect-button/SlackConnectButton.tsxpackages/js/src/ui/components/slack-link-user/SlackLinkUser.tsx
Problem
When
SlackConnectButton,MsTeamsConnectButton,SlackLinkUser, orMsTeamsLinkUserare rendered without an explicitconnectionIdentifierprop, all subscribers in the same environment shared a single hardcoded default:chconn-slack-defaultchconn-msteams-defaultThe
channel_connectionscollection enforces a unique index on{ _environmentId, identifier }. This means the second subscriber in an environment to click Connect would receive a409 Conflictfrom the API and be silently unable to connect — even though their connection was for a completely different subscriber.The root cause is that the API's
CreateChannelConnectionuse-case has two separate uniqueness guards:(integration + subscriberId + contextKeys)— which correctly allows a second subscriber through{ _environmentId, identifier }— which blocks the second subscriber because both default to the same static stringSolution
Derive the default
connectionIdentifieras<prefix>-<subscriberId>using a newbuildDefaultConnectionIdentifierhelper, so each subscriber gets their own scoped default. Falls back to the bare prefix whensubscriberIdis unavailable (shared mode), preserving existing behaviour for that edge case.No backend changes, no DB migration, no API breaking change.
Files changed
packages/js/src/ui/components/constants.ts— addedbuildDefaultConnectionIdentifier(prefix, subscriberId)SlackConnectButton.tsx— use per-subscriber defaultMsTeamsConnectButton.tsx— use per-subscriber defaultSlackLinkUser.tsx— use per-subscriber defaultMsTeamsLinkUser.tsx— use per-subscriber defaultBehavior after this fix
chconn-slack-defaultchconn-slack-default-user-Achconn-slack-default-user-BconnectionIdentifierchconn-slack-defaultTest plan
SlackConnectButtonwithout providingconnectionIdentifier— both should connect successfullychconn-slack-default-<subscriberId>connectionIdentifierstill uses that value unchangedSlackLinkUserandMsTeamsLinkUserbehave the same way