Files
twenty/packages/twenty-apps/public/slack/src/logic-functions/handlers/slack-app-uninstall-handler.ts
T
Abdul Rahman 577cd68cb5 feat(slack): release the Slack team claim when the connection goes away (#23753)
Closes the loop on
https://github.com/twentyhq/twenty/pull/22984#discussion_r3673946334,
where the Slack app claimed a `team_id` on connect and never released
it. The `onDisconnect` hook it waited on merged in #23538, and
`twenty-sdk` 2.27.0 ships the `onDisconnectLogicFunction` manifest
field, so this bumps the app to it.

## Disconnect

`slack-team-release` releases the server-scoped `slack-team:{teamId}`
claim, so another Twenty workspace can connect that Slack team
afterwards. `kv.delete` on a SERVER key only clears a claim the calling
workspace owns, and the result reports a team only when that delete
actually removed something.

The connection is already deleted when the hook runs, so `getConnection`
cannot resolve the `team_id` anymore. `claimSlackTeam` records `teamId`
under a workspace-scoped
`slack-connected-account-team:{connectedAccountId}` key, written before
the claim itself so a failure between the two cannot leave a claim
nothing can resolve, and the release reads it back from there. That also
retires the TODO the review comment pointed at.

The release is skipped when a live connection still maps to the same
team, excluding the disconnecting account explicitly. That covers a
reconnect that re-claimed the team while the job was queued, and a
second connection to the same Slack workspace, whose claim would
otherwise have been dropped along with the first. The recorded team is
cleared either way, so the disconnecting account leaves nothing behind.

## Uninstall

Uninstalling the app drops its connections through the
`connectionProvider` and `application` cascades, which never reach the
disconnect hook, and the same operation deletes the app's logic
functions so the hook could not run anyway. The app declares an
`uninstallLogicFunction`: it runs before the app's metadata is
deleted, lists the remaining Slack connections, and releases each claim
through the same `releaseSlackTeamClaim` util. It deliberately skips the
live-connection check, since at that point every connection is still
listed.

## Known limits

A disconnect immediately followed by a reconnect has a window where the
queued release can clear a claim the reconnect just took. It self-heals,
because the reconnect's own onConnect re-claims the same key moments
later. Closing it properly needs a compare-and-set on the claim, which
the key-value store does not expose.

No unit tests are added for the release path. Earlier revisions of this
branch had specs for the disconnect guard, the uninstall sweep and the
no-op-release result; they were dropped, so those branches carry no
regression coverage. The app's existing 34 tests still pass, along
with lint, typecheck and `twenty dev:build`, the last being what used to
fail with `Duplicate universal identifiers` on the older SDK.
2026-08-06 13:57:33 +00:00

4 lines
161 B
TypeScript

import { releaseAllSlackTeams } from 'src/logic-functions/utils/release-all-slack-teams';
export const slackAppUninstallHandler = () => releaseAllSlackTeams();