-
Notifications
You must be signed in to change notification settings - Fork 619
Add reason code type to team capabilities response #7921
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
Add reason code type to team capabilities response #7921
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 463f5c8 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
WalkthroughAdds a ReasonCode type and EnabledWithReason, updates TeamCapabilities to use it (including nested websockets), changes authorize() to return specific reason-based 402/403 responses, and updates dashboard stubs and service mocks and a changeset documenting the API-type addition. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Client
participant Service as API Endpoint
participant Auth as authorize()
participant Caps as getServiceDisabledReason()
participant Key as Key Validator
Client->>Service: Request (team scope)
Service->>Auth: authorize(request, scope, capabilities)
rect rgba(230,240,255,0.5)
note over Auth,Caps: Check capability state (EnabledWithReason)
Auth->>Caps: Evaluate scope in capabilities
Caps-->>Auth: ReasonCode | null
end
alt Disabled (ReasonCode)
note right of Auth: Map reason → 402/403 + URL
Auth-->>Service: Error { status: 402/403, code, message }
Service-->>Client: Error response
else Enabled (null)
Auth->>Key: Validate API key / permissions
Key-->>Auth: OK / Error
alt Key OK
Auth-->>Service: Proceed
Service-->>Client: Success
else Key Error
Auth-->>Service: 401/403
Service-->>Client: Error
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #7921 +/- ##
=======================================
Coverage 56.53% 56.53%
=======================================
Files 904 904
Lines 58592 58592
Branches 4143 4143
=======================================
Hits 33126 33126
Misses 25360 25360
Partials 106 106
🚀 New features to boost your workflow:
|
7a6353e to
a3cb5f3
Compare
size-limit report 📦
|
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: 3
🧹 Nitpick comments (2)
packages/service-utils/src/core/api.ts (1)
60-67: Make disabled branch Partial to mirror real API payloads.If the API omits capacity fields when a capability is disabled, the current intersection forces those fields to exist. Safer to require them only when enabled.
-type EnabledWithReason<T> = T & - ({ enabled: true } | { enabled: false; reasonCode: ReasonCode }); +type EnabledWithReason<T> = + | (T & { enabled: true }) + | (Partial<T> & { enabled: false; reasonCode: ReasonCode });packages/service-utils/src/core/authorize/index.ts (1)
148-189: Optional: deduplicate error message URLs to avoid drift.Extract small helpers (billingUrl, invoicesUrl, supportUrl) or a map keyed by ReasonCode.
I can push a follow-up diff if you want the helpers added now.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
.changeset/fuzzy-bars-wish.md(1 hunks)apps/dashboard/src/@/storybook/stubs.ts(1 hunks)packages/service-utils/src/core/api.ts(1 hunks)packages/service-utils/src/core/authorize/index.ts(3 hunks)packages/service-utils/src/mocks.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial,Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Files:
packages/service-utils/src/mocks.tsapps/dashboard/src/@/storybook/stubs.tspackages/service-utils/src/core/authorize/index.tspackages/service-utils/src/core/api.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
packages/service-utils/src/mocks.tsapps/dashboard/src/@/storybook/stubs.tspackages/service-utils/src/core/authorize/index.tspackages/service-utils/src/core/api.ts
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/*(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/dashboard/src/@/storybook/stubs.ts
🧬 Code graph analysis (1)
packages/service-utils/src/core/authorize/index.ts (1)
packages/service-utils/src/core/api.ts (3)
CoreServiceConfig(18-34)TeamResponse(136-168)ReasonCode(60-65)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Analyze (javascript)
- GitHub Check: Size
🔇 Additional comments (3)
packages/service-utils/src/core/api.ts (1)
74-81: LGTM: rpc.websockets is modeled with nested EnabledWithReason.The nested gating reads clearly and aligns with the new pattern.
packages/service-utils/src/mocks.ts (1)
95-100: LGTM: stub reflects disabled websockets with a specific reasonCode.Shape matches EnabledWithReason and helps exercise the 402/403 pathing.
apps/dashboard/src/@/storybook/stubs.ts (1)
77-82: LGTM: dashboard stub mirrors the new rpc.websockets capability.Consistent with service-utils typing and mocks.
a3cb5f3 to
463f5c8
Compare
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
♻️ Duplicate comments (1)
packages/service-utils/src/core/authorize/index.ts (1)
143-147: Pass serviceAction so sub-feature (e.g., rpc websockets) can return accurate reason codes.Currently only top-level scope is checked; nested disables will be missed.
- const disabledReason = getServiceDisabledReason( - serviceConfig.serviceScope, - teamAndProjectResponse.team.capabilities, - ); + const disabledReason = getServiceDisabledReason( + serviceConfig.serviceScope, + teamAndProjectResponse.team.capabilities, + serviceConfig.serviceAction, + );
🧹 Nitpick comments (2)
packages/service-utils/src/core/api.ts (1)
60-67: Export EnabledWithReason for reuse across modules.Other modules (e.g., mocks, consumers defining sub-capabilities) will likely need this utility type.
-export type ReasonCode = +export type ReasonCode = | "free_limit_exceeded" | "subscription_required" | "invoice_past_due" | "enterprise_plan_required" | "other"; -type EnabledWithReason<T> = T & +export type EnabledWithReason<T> = T & ({ enabled: true } | { enabled: false; reasonCode: ReasonCode });packages/service-utils/src/core/authorize/index.ts (1)
148-189: Safely embed team slug in URLs and DRY the message prefix.Use encodeURIComponent to avoid malformed URLs if slug ever contains unexpected chars, and centralize the slug to reduce repetition.
- if (disabledReason) { + if (disabledReason) { + const teamSlug = encodeURIComponent(teamAndProjectResponse.team.slug); switch (disabledReason) { case "enterprise_plan_required": { return { authorized: false, errorCode: "ENTERPRISE_PLAN_REQUIRED", - errorMessage: `You currently do not have access to this feature. Please reach out to us to upgrade your plan to enable this feature: https://thirdweb.com/team/${teamAndProjectResponse.team.slug}/~/support`, + errorMessage: `You currently do not have access to this feature. Please reach out to us to upgrade your plan to enable this feature: https://thirdweb.com/team/${teamSlug}/~/support`, status: 402, }; } case "free_limit_exceeded": { return { authorized: false, errorCode: "FREE_LIMIT_EXCEEDED", - errorMessage: `You have exceeded the free limit for this service. Find a plan that suits your needs to continue using this feature: https://thirdweb.com/team/${teamAndProjectResponse.team.slug}/~/billing?showPlans=true&highlight=growth`, + errorMessage: `You have exceeded the free limit for this service. Find a plan that suits your needs to continue using this feature: https://thirdweb.com/team/${teamSlug}/~/billing?showPlans=true&highlight=growth`, status: 402, }; } case "subscription_required": { return { authorized: false, errorCode: "SUBSCRIPTION_REQUIRED", - errorMessage: `You need a subscription to use this feature. Find a plan that suits your needs to continue using this feature: https://thirdweb.com/team/${teamAndProjectResponse.team.slug}/~/billing?showPlans=true&highlight=growth`, + errorMessage: `You need a subscription to use this feature. Find a plan that suits your needs to continue using this feature: https://thirdweb.com/team/${teamSlug}/~/billing?showPlans=true&highlight=growth`, status: 402, }; } case "invoice_past_due": { return { authorized: false, errorCode: "INVOICE_PAST_DUE", - errorMessage: `Please pay any outstanding invoices to continue using this feature: https://thirdweb.com/team/${teamAndProjectResponse.team.slug}/~/billing/invoices`, + errorMessage: `Please pay any outstanding invoices to continue using this feature: https://thirdweb.com/team/${teamSlug}/~/billing/invoices`, status: 402, }; } default: { return { authorized: false, errorCode: "SERVICE_TEMPORARILY_DISABLED", - errorMessage: `Access to this feature is temporarily restricted. Please reach out to us to resolve this issue: https://thirdweb.com/team/${teamAndProjectResponse.team.slug}/~/support`, + errorMessage: `Access to this feature is temporarily restricted. Please reach out to us to resolve this issue: https://thirdweb.com/team/${teamSlug}/~/support`, status: 403, }; } } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
.changeset/fuzzy-bars-wish.md(1 hunks)apps/dashboard/src/@/storybook/stubs.ts(1 hunks)packages/service-utils/src/core/api.ts(1 hunks)packages/service-utils/src/core/authorize/index.ts(3 hunks)packages/service-utils/src/mocks.ts(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- .changeset/fuzzy-bars-wish.md
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/service-utils/src/mocks.ts
- apps/dashboard/src/@/storybook/stubs.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial,Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Files:
packages/service-utils/src/core/api.tspackages/service-utils/src/core/authorize/index.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
packages/service-utils/src/core/api.tspackages/service-utils/src/core/authorize/index.ts
🧬 Code graph analysis (1)
packages/service-utils/src/core/authorize/index.ts (1)
packages/service-utils/src/core/api.ts (3)
CoreServiceConfig(18-34)TeamResponse(136-168)ReasonCode(60-65)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: E2E Tests (pnpm, esbuild)
- GitHub Check: Size
- GitHub Check: E2E Tests (pnpm, vite)
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: Lint Packages
- GitHub Check: Unit Tests
- GitHub Check: Analyze (javascript)
🔇 Additional comments (4)
packages/service-utils/src/core/api.ts (2)
56-59: Doc tweak LGTM.Wording update is clear and accurate.
74-81: Modeling for rpc.websockets is good; ensure auth path enforces sub-capability gating.Types now allow websockets to be disabled while rpc is enabled. Verify authorize() checks nested websockets when serviceAction indicates it. See my suggested fix in authorize/index.ts.
packages/service-utils/src/core/authorize/index.ts (2)
4-4: Type-only import LGTM.Keeps it out of runtime bundles.
225-266: UpdategetServiceDisabledReasoncall and verify new capability fieldsThe change to add an optional
serviceActionparameter is a breaking change. You must update the existing invocation inpackages/service-utils/src/core/authorize/index.ts(around line 143) to pass the new argument, and ensure the newrpc.websockets,mcp, andgatewaycapability fields actually exist on yourTeamResponse["capabilities"]type.• In authorize/index.ts, change:
const disabledReason = getServiceDisabledReason( serviceConfig.serviceScope, teamAndProjectResponse.team.capabilities, );to something like:
const disabledReason = getServiceDisabledReason( serviceConfig.serviceScope, teamAndProjectResponse.team.capabilities, serviceConfig.serviceAction, // new param );• Confirm that
–teamCapabilities.rpc.websockets(with its.enabledand.reasonCode) is defined in yourTeamResponse.capabilitiestype.
–teamCapabilities.mcpandteamCapabilities.gatewayexist and follow the same shape (enabled,reasonCode).
– Adjust your type definitions or property names if any of these fields are missing or named differently.Please verify those call sites and type definitions to avoid build errors or missing checks.

Add reason code type into team capabilities response
This PR enhances the team capabilities response by:
ReasonCodetype with specific values like "free_limit_exceeded", "subscription_required", etc.EnabledWithReason<T>generic type that includes a reason code when a capability is disabledThese changes allow for more detailed feedback to users when a service is disabled, with specific guidance based on the reason (e.g., directing to billing page for payment issues or support for enterprise features).
PR-Codex overview
This PR introduces a
reasonCodefor team capabilities responses, enhancing the error handling for service access based on subscription status. It modifies the type definitions and adds specific error messages based on different reasons for service unavailability.Detailed summary
reasonCodetowebsocketsandstoragecapabilities.ReasonCodetype with specific reasons.EnabledWithReasontype to includereasonCode.authorizefunction to handle specific error cases based onreasonCode.isServiceEnabledForTeamtogetServiceDisabledReason, returningReasonCodeinstead of a boolean.Summary by CodeRabbit