From 496ee70bc2e027b23b36252e52262face510edd9 Mon Sep 17 00:00:00 2001 From: kemal Date: Mon, 7 Sep 2026 14:49:18 +0100 Subject: [PATCH] feat: canonical scope badges and no upfront over-role annotation Badge labels are now exactly READ, WRITE and READ-WRITE, replacing the READ + WRITE spelling. Role validation is post-submit only, so the upfront "Read-only for your role" annotation and its isScopeGroupOverRole helper are removed along with the memberRole prop that only fed them. The post-submit annotation lands with PROD-653. Co-Authored-By: Claude Opus 5 (1M context) --- .../Consent/OverRoleAnnotation.test.tsx | 22 --------------- .../OAuthApps/Consent/OverRoleAnnotation.tsx | 13 --------- .../Consent/OverRoleAnnotation.utils.test.ts | 20 ------------- .../Consent/OverRoleAnnotation.utils.ts | 10 ------- .../OAuthApps/Consent/ScopeGroupCard.test.tsx | 28 +++++++++---------- .../OAuthApps/Consent/ScopeGroupCard.tsx | 21 ++++---------- .../Organization/OAuthApps/Consent/index.ts | 3 -- 7 files changed, 19 insertions(+), 98 deletions(-) delete mode 100644 apps/studio/components/interfaces/Organization/OAuthApps/Consent/OverRoleAnnotation.test.tsx delete mode 100644 apps/studio/components/interfaces/Organization/OAuthApps/Consent/OverRoleAnnotation.tsx delete mode 100644 apps/studio/components/interfaces/Organization/OAuthApps/Consent/OverRoleAnnotation.utils.test.ts delete mode 100644 apps/studio/components/interfaces/Organization/OAuthApps/Consent/OverRoleAnnotation.utils.ts diff --git a/apps/studio/components/interfaces/Organization/OAuthApps/Consent/OverRoleAnnotation.test.tsx b/apps/studio/components/interfaces/Organization/OAuthApps/Consent/OverRoleAnnotation.test.tsx deleted file mode 100644 index 4599c959ce8..00000000000 --- a/apps/studio/components/interfaces/Organization/OAuthApps/Consent/OverRoleAnnotation.test.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { screen } from '@testing-library/react' -import { describe, expect, test } from 'vitest' - -import { OverRoleAnnotation } from './OverRoleAnnotation' -import { customRender } from '@/tests/lib/custom-render' - -describe('OverRoleAnnotation', () => { - test('renders when the requested level exceeds the role', () => { - customRender() - expect(screen.getByText('Read-only for your role')).toBeInTheDocument() - }) - - test('renders nothing when the role covers the requested level', () => { - customRender() - expect(screen.queryByText('Read-only for your role')).not.toBeInTheDocument() - }) - - test('renders nothing for a role that can write', () => { - customRender() - expect(screen.queryByText('Read-only for your role')).not.toBeInTheDocument() - }) -}) diff --git a/apps/studio/components/interfaces/Organization/OAuthApps/Consent/OverRoleAnnotation.tsx b/apps/studio/components/interfaces/Organization/OAuthApps/Consent/OverRoleAnnotation.tsx deleted file mode 100644 index a3d07034834..00000000000 --- a/apps/studio/components/interfaces/Organization/OAuthApps/Consent/OverRoleAnnotation.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { isScopeGroupOverRole } from './OverRoleAnnotation.utils' -import type { OAuthOrganizationRole, OAuthScopeLevel } from '@/data/oauth-apps/types' - -export interface OverRoleAnnotationProps { - level: OAuthScopeLevel - memberRole: OAuthOrganizationRole['role'] -} - -export const OverRoleAnnotation = ({ level, memberRole }: OverRoleAnnotationProps) => { - if (!isScopeGroupOverRole(level, memberRole)) return null - - return Read-only for your role -} diff --git a/apps/studio/components/interfaces/Organization/OAuthApps/Consent/OverRoleAnnotation.utils.test.ts b/apps/studio/components/interfaces/Organization/OAuthApps/Consent/OverRoleAnnotation.utils.test.ts deleted file mode 100644 index 01c8e5c937a..00000000000 --- a/apps/studio/components/interfaces/Organization/OAuthApps/Consent/OverRoleAnnotation.utils.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { describe, expect, test } from 'vitest' - -import { isScopeGroupOverRole } from './OverRoleAnnotation.utils' - -describe('isScopeGroupOverRole', () => { - test('flags write and read_write levels for a Read-only role', () => { - expect(isScopeGroupOverRole('write', 'Read-only')).toBe(true) - expect(isScopeGroupOverRole('read_write', 'Read-only')).toBe(true) - }) - - test('does not flag a read level for a Read-only role', () => { - expect(isScopeGroupOverRole('read', 'Read-only')).toBe(false) - }) - - test('does not flag any level for roles that can write', () => { - expect(isScopeGroupOverRole('read_write', 'Developer')).toBe(false) - expect(isScopeGroupOverRole('write', 'Owner')).toBe(false) - expect(isScopeGroupOverRole('read', 'Developer')).toBe(false) - }) -}) diff --git a/apps/studio/components/interfaces/Organization/OAuthApps/Consent/OverRoleAnnotation.utils.ts b/apps/studio/components/interfaces/Organization/OAuthApps/Consent/OverRoleAnnotation.utils.ts deleted file mode 100644 index 1d8daa72c51..00000000000 --- a/apps/studio/components/interfaces/Organization/OAuthApps/Consent/OverRoleAnnotation.utils.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { OAuthOrganizationRole, OAuthScopeLevel } from '@/data/oauth-apps/types' - -// Role capability is binary for now: a "Read-only" role can't satisfy write scopes, and every -// other role (Developer, Owner, ...) is assumed to satisfy whatever the app requests. -export function isScopeGroupOverRole( - level: OAuthScopeLevel, - role: OAuthOrganizationRole['role'] -): boolean { - return role === 'Read-only' && level !== 'read' -} diff --git a/apps/studio/components/interfaces/Organization/OAuthApps/Consent/ScopeGroupCard.test.tsx b/apps/studio/components/interfaces/Organization/OAuthApps/Consent/ScopeGroupCard.test.tsx index 9633485eab8..6935404b829 100644 --- a/apps/studio/components/interfaces/Organization/OAuthApps/Consent/ScopeGroupCard.test.tsx +++ b/apps/studio/components/interfaces/Organization/OAuthApps/Consent/ScopeGroupCard.test.tsx @@ -20,9 +20,7 @@ const SCOPE_GROUPS: OAuthScopeGroup[] = [ describe('ScopeGroupCard', () => { test('interpolates the app name into the intro line', () => { - customRender( - - ) + customRender() expect( screen.getByText( @@ -31,28 +29,28 @@ describe('ScopeGroupCard', () => { ).toBeInTheDocument() }) - test('renders READ and READ + WRITE badge labels', () => { - customRender( - - ) + test('renders READ and READ-WRITE badge labels', () => { + customRender() - expect(screen.getByText('READ + WRITE')).toBeInTheDocument() + expect(screen.getByText('READ-WRITE')).toBeInTheDocument() expect(screen.getByText('READ')).toBeInTheDocument() }) - test('shows the over-role annotation only for groups that exceed a Read-only role', () => { + test('renders a WRITE badge for a write-only group', () => { customRender( - + ) - expect(screen.getAllByText('Read-only for your role')).toHaveLength(1) + expect(screen.getByText('WRITE')).toBeInTheDocument() }) - test('shows no over-role annotation for a role that can write', () => { - customRender( - - ) + test('renders no over-role annotation', () => { + customRender() expect(screen.queryByText('Read-only for your role')).not.toBeInTheDocument() + expect(screen.queryByText('READ + WRITE')).not.toBeInTheDocument() }) }) diff --git a/apps/studio/components/interfaces/Organization/OAuthApps/Consent/ScopeGroupCard.tsx b/apps/studio/components/interfaces/Organization/OAuthApps/Consent/ScopeGroupCard.tsx index 4881c3094b3..bf26ce2911c 100644 --- a/apps/studio/components/interfaces/Organization/OAuthApps/Consent/ScopeGroupCard.tsx +++ b/apps/studio/components/interfaces/Organization/OAuthApps/Consent/ScopeGroupCard.tsx @@ -1,19 +1,13 @@ import { Badge, Card, CardContent } from 'ui' -import { OverRoleAnnotation } from './OverRoleAnnotation' -import type { - OAuthOrganizationRole, - OAuthScopeGroup, - OAuthScopeLevel, -} from '@/data/oauth-apps/types' +import type { OAuthScopeGroup, OAuthScopeLevel } from '@/data/oauth-apps/types' export interface ScopeGroupCardProps { appName: string scopeGroups: OAuthScopeGroup[] - memberRole: OAuthOrganizationRole['role'] } -export const ScopeGroupCard = ({ appName, scopeGroups, memberRole }: ScopeGroupCardProps) => { +export const ScopeGroupCard = ({ appName, scopeGroups }: ScopeGroupCardProps) => { return (
@@ -30,12 +24,9 @@ export const ScopeGroupCard = ({ appName, scopeGroups, memberRole }: ScopeGroupC
{scopeGroups.map((scopeGroup) => (
-
- - {getScopeLevelLabel(scopeGroup.level)} - - -
+ + {getScopeLevelLabel(scopeGroup.level)} +

{scopeGroup.scopes.join(', ')}

))} @@ -49,7 +40,7 @@ export const ScopeGroupCard = ({ appName, scopeGroups, memberRole }: ScopeGroupC function getScopeLevelLabel(level: OAuthScopeLevel) { if (level === 'read') return 'READ' if (level === 'write') return 'WRITE' - return 'READ + WRITE' + return 'READ-WRITE' } function getScopeLevelBadgeVariant(level: OAuthScopeLevel) { diff --git a/apps/studio/components/interfaces/Organization/OAuthApps/Consent/index.ts b/apps/studio/components/interfaces/Organization/OAuthApps/Consent/index.ts index d719bf886d7..9fc667e22ef 100644 --- a/apps/studio/components/interfaces/Organization/OAuthApps/Consent/index.ts +++ b/apps/studio/components/interfaces/Organization/OAuthApps/Consent/index.ts @@ -1,5 +1,2 @@ -export { OverRoleAnnotation } from './OverRoleAnnotation' -export type { OverRoleAnnotationProps } from './OverRoleAnnotation' -export { isScopeGroupOverRole } from './OverRoleAnnotation.utils' export { ScopeGroupCard } from './ScopeGroupCard' export type { ScopeGroupCardProps } from './ScopeGroupCard'