-
Notifications
You must be signed in to change notification settings - Fork 0
refactor: extract subscription handler switch cases to functions #43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Extract switch case logic into dedicated handler functions for better organization and maintainability: - handleSubscribe() - handles repository subscription with OAuth and installation flow - handleUnsubscribe() - handles repository unsubscription - handleStatus() - displays channel subscriptions Also added ephemeral OAuth URLs (security fix) to prevent account hijacking vulnerability. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
WalkthroughThis PR replaces a monolithic GitHub subscription handler with a modular implementation in src/handlers/github-subscription-handler.ts. It exports a public handleGithubSubscription(handler, event, subscriptionService) that dispatches to internal handlers for subscribe, unsubscribe, and status. New helpers parseEventTypes and formatEventTypes are added; subscribe/unsubscribe/status flows are implemented with repo validation, OAuth/initiation for installs, GitHub App installation handling for private repos, and unified usage/error messaging. Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Files/areas to pay extra attention:
Possibly related PRs
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (3)
src/handlers/github-subscription-handler.ts (3)
55-163: Subscribe handler logic and OAuth/installation flows look solid
- Repo handling is careful: you strip markdown (
stripMarkdown) and validateowner/reposhape before touching the service, which avoids garbage going intoSubscriptionService.- Event-type parsing is centralized via
parseEventTypes(args)and properly wrapped in atry/catch, so invalid flags yield a clear, single error message to the user instead of throwing.- The OAuth requirement branch sends the auth URL via
handler.sendMessage(..., { ephemeral: true }), which matches the PR goal of preventing OAuth URL leakage in the channel.- Private-repo installation requirements and public-repo “suggest install for webhooks” paths are surfaced with detailed, actionable copy; success copy includes delivery mode and the GitHub link.
One minor behavioral nit you might consider (optional): if a user supplies
--eventswithout any value,parseEventTypesfalls back toDEFAULT_EVENT_TYPESrather than treating it as an error, which may hide a typo in the command. If you want stricter UX, you could detect--eventswith no value and throw instead.If you want to double-check the ephemeral behavior of
handler.sendMessage, you can confirm in@towns-protocol/botdocs that{ ephemeral: true }indeed restricts visibility to the invoking user only.
238-269: Status handler reads well; consider future-proofing for very long lists (optional)The status handler correctly:
- Fetches channel subscriptions once,
- Returns a friendly “no subscriptions” message, and
- Renders each repo with a mode icon and formatted event types via
formatEventTypes.For typical channel sizes, this is great. If you anticipate channels subscribing to many dozens of repos, you might later want to paginate or truncate
repoListto avoid overly long messages or platform max-length issues, but that’s an optimization you can defer.
276-324: Event-type parsing is robust; minor edge cases aroundalland empty valuesThe
parseEventTypeshelper is nicely self-contained and handles:
- Both
--events=pr,issuesand--events pr,issuessyntax,- Defaults when the flag is absent,
- Normalization to lowercase with trimming,
- Validation against
ALLOWED_EVENT_TYPES, and- Expansion of
allto the full set.Two small behavioral notes (optional, not blockers):
allplus typos is silently accepted.
--events=foo,all,barresults intokens.includes("all")and immediately returnsALLOWED_EVENT_TYPES.join(","), skipping validation offoo/bar. If you’d rather fail on typos whenallis combined with other values, validate first and only treatallspecially if it’s the only token.Bare
--eventsfalls back to default.
As with the subscribe handler comment,--eventswithout a value returnsDEFAULT_EVENT_TYPES. That’s defensible, but if you want tighter UX you could instead throw an error to signal a malformed flag.If you’re happy with the current semantics, the implementation is otherwise clear and performant.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/handlers/github-subscription-handler.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/handlers/github-subscription-handler.ts (3)
src/types/bot.ts (1)
SlashCommandEvent(13-15)src/services/subscription-service.ts (1)
SubscriptionService(47-356)src/utils/stripper.ts (1)
stripMarkdown(6-13)
🔇 Additional comments (2)
src/handlers/github-subscription-handler.ts (2)
11-50: Top-level dispatcher is clear and keeps public API smallThe main
handleGithubSubscriptionfunction cleanly parsesaction, provides a helpful usage message when missing, and routes to the three internal handlers with minimal branching. The unknown-action message is explicit and constrained to the supported verbs, which is good for UX. No issues from a correctness or security perspective here.
329-333: Event-type display formatter is simple and sufficient
formatEventTypesjust normalizes spacing around commas, which is exactly what’s needed for user-facing messages and keeps behavior predictable across handlers. No issues here.
Use case-insensitive matching to find subscriptions and the canonical repo name from DB for deletion. Prevents failures when users type different casing (e.g., "OWNER/repo" vs "owner/repo"). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
src/handlers/github-subscription-handler.ts (3)
19-27: Deduplicate usage/help strings to avoid driftUsage/help text for
/github(full usage, subscribe, unsubscribe, status) is hard-coded in several places. This is easy to let drift from the actual behavior or allowed event types.Consider extracting a shared
const GITHUB_USAGE = ...(or a small helper) and, ideally, deriving the event-types portion fromALLOWED_EVENT_TYPESso adding/removing types in the constants automatically updates the help text.Also applies to: 63-69, 176-181, 254-257
63-82: Tighten repo sanitization and handle empty--eventsedge caseTwo small robustness tweaks worth considering:
- Repo sanitization:
stripMarkdownremoves bold/italic/code markers but not angle brackets or leading/trailing whitespace. Inputs like<owner/repo>or" owner/repo "will pass the/check but produce invalidowner/reposegments forsubscribeToRepository. A small normalization step would help:- const repo = stripMarkdown(repoArg); + const repo = stripMarkdown(repoArg).trim().replace(/^<|>$/g, "");
- Empty
--eventstokens:
For--events ,or--events=,,tokensends up empty andparseEventTypescurrently returns"", which gets stored as an empty string while other paths treat falsy values as “use defaults”. You can normalize this by short‑circuiting whentokens.length === 0:const tokens = rawEventTypes .split(",") .map(token => token.trim().toLowerCase()) .filter(token => token.length > 0); + if (tokens.length === 0) { + return DEFAULT_EVENT_TYPES; + }This keeps DB state consistent with the runtime behavior.
Also applies to: 83-93, 280-328
249-273: Optional: sort status output for readability
handleStatuscorrectly lists all channel subscriptions, including delivery mode and event types. For channels with many repos, the list might be easier to scan if it were sorted (e.g., alphabetically bysub.repoor grouped bydeliveryMode):- const repoList = subscriptions - .map(sub => { + const repoList = [...subscriptions] + .sort((a, b) => a.repo.localeCompare(b.repo)) + .map(sub => { const mode = sub.deliveryMode === "webhook" ? "⚡" : "⏱️"; return `${mode} ${sub.repo} (${formatEventTypes(sub.eventTypes)})`; })Not required, but could improve UX in busy channels.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/handlers/github-subscription-handler.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/handlers/github-subscription-handler.ts (3)
src/types/bot.ts (1)
SlashCommandEvent(13-15)src/services/subscription-service.ts (1)
SubscriptionService(47-356)src/utils/stripper.ts (1)
stripMarkdown(6-13)
🔇 Additional comments (2)
src/handlers/github-subscription-handler.ts (2)
103-113: Ephemeral OAuth auth URL handling looks solidUsing
{ ephemeral: true }for the OAuth “Connect GitHub Account” message ensures the authorization URL is only visible to the requesting user, which directly mitigates link-leakage/account-hijack style issues described in the PR. No further changes needed here.
196-227: Unsubscribe now correctly handles repo casing and canonical namesThe updated unsubscribe flow:
- Checks channel subscriptions up front.
- Locates the subscription with a case-insensitive match (
sub.repo.toLowerCase() === repo.toLowerCase()).- Calls
subscriptionService.unsubscribewithsubscription.repo(the canonical DB value) while still echoing the user’s originalrepoin messages.This resolves the prior case-sensitivity issue and guarantees the correct row is targeted without impacting UX.
Also applies to: 229-237
Extract switch case logic into dedicated handler functions for better organization and maintainability:
Also added ephemeral OAuth URLs (security fix) to prevent account hijacking vulnerability.
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.