mirror of
https://github.com/supabase/supabase.git
synced 2026-10-08 19:05:06 +03:00
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) <noreply@anthropic.com>
This commit is contained in:
1 parent
7ee09f1b06
commit
496ee70bc2
7 files changed
+19
-98
No files matched your search
-22
@@ -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(<OverRoleAnnotation level="write" memberRole="Read-only" />)
|
||||
expect(screen.getByText('Read-only for your role')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test('renders nothing when the role covers the requested level', () => {
|
||||
customRender(<OverRoleAnnotation level="read" memberRole="Read-only" />)
|
||||
expect(screen.queryByText('Read-only for your role')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
test('renders nothing for a role that can write', () => {
|
||||
customRender(<OverRoleAnnotation level="read_write" memberRole="Developer" />)
|
||||
expect(screen.queryByText('Read-only for your role')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
-13
@@ -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 <span className="text-xs text-foreground-lighter shrink-0">Read-only for your role</span>
|
||||
}
|
||||
-20
@@ -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)
|
||||
})
|
||||
})
|
||||
-10
@@ -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'
|
||||
}
|
||||
+13
-15
@@ -20,9 +20,7 @@ const SCOPE_GROUPS: OAuthScopeGroup[] = [
|
||||
|
||||
describe('ScopeGroupCard', () => {
|
||||
test('interpolates the app name into the intro line', () => {
|
||||
customRender(
|
||||
<ScopeGroupCard appName="Vercel" scopeGroups={SCOPE_GROUPS} memberRole="Developer" />
|
||||
)
|
||||
customRender(<ScopeGroupCard appName="Vercel" scopeGroups={SCOPE_GROUPS} />)
|
||||
|
||||
expect(
|
||||
screen.getByText(
|
||||
@@ -31,28 +29,28 @@ describe('ScopeGroupCard', () => {
|
||||
).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test('renders READ and READ + WRITE badge labels', () => {
|
||||
customRender(
|
||||
<ScopeGroupCard appName="Vercel" scopeGroups={SCOPE_GROUPS} memberRole="Developer" />
|
||||
)
|
||||
test('renders READ and READ-WRITE badge labels', () => {
|
||||
customRender(<ScopeGroupCard appName="Vercel" scopeGroups={SCOPE_GROUPS} />)
|
||||
|
||||
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(
|
||||
<ScopeGroupCard appName="Vercel" scopeGroups={SCOPE_GROUPS} memberRole="Read-only" />
|
||||
<ScopeGroupCard
|
||||
appName="Vercel"
|
||||
scopeGroups={[{ name: 'Logs', level: 'write', scopes: ['logs'] }]}
|
||||
/>
|
||||
)
|
||||
|
||||
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(
|
||||
<ScopeGroupCard appName="Vercel" scopeGroups={SCOPE_GROUPS} memberRole="Developer" />
|
||||
)
|
||||
test('renders no over-role annotation', () => {
|
||||
customRender(<ScopeGroupCard appName="Vercel" scopeGroups={SCOPE_GROUPS} />)
|
||||
|
||||
expect(screen.queryByText('Read-only for your role')).not.toBeInTheDocument()
|
||||
expect(screen.queryByText('READ + WRITE')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
+6
-15
@@ -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 (
|
||||
<section className="flex flex-col">
|
||||
<div>
|
||||
@@ -30,12 +24,9 @@ export const ScopeGroupCard = ({ appName, scopeGroups, memberRole }: ScopeGroupC
|
||||
<div className="divide-y divide-muted px-4">
|
||||
{scopeGroups.map((scopeGroup) => (
|
||||
<div key={scopeGroup.name} className="py-3">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<Badge variant={getScopeLevelBadgeVariant(scopeGroup.level)}>
|
||||
{getScopeLevelLabel(scopeGroup.level)}
|
||||
</Badge>
|
||||
<OverRoleAnnotation level={scopeGroup.level} memberRole={memberRole} />
|
||||
</div>
|
||||
<Badge variant={getScopeLevelBadgeVariant(scopeGroup.level)}>
|
||||
{getScopeLevelLabel(scopeGroup.level)}
|
||||
</Badge>
|
||||
<p className="mt-1 text-sm text-foreground-light">{scopeGroup.scopes.join(', ')}</p>
|
||||
</div>
|
||||
))}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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'
|
||||
Reference in new issue
Block a user