feat(a2a): added a2a protocol (#2784)

* feat(a2a): a2a added

* feat(a2a): added a2a protocol

* remove migrations

* readd migrations

* consolidated permissions utils

* consolidated tag-input, output select -> combobox, added tags for A2A

* cleanup up utils, share same deployed state as other tabs

* ack PR comments

* more

* updated code examples

* solely rely on tanstack query to vend data and invalidate query key's, remove custom caching

---------

Co-authored-by: Emir Karabeg <emirkarabeg@berkeley.edu>
This commit is contained in:
Waleed
2026-01-13 11:43:02 -08:00
committed by GitHub
parent 40a066f39c
commit 2bc403972c
70 changed files with 17887 additions and 481 deletions

View File

@@ -40,11 +40,15 @@ vi.mock('drizzle-orm', () => drizzleOrmMock)
import { db } from '@sim/db'
import {
checkWorkspaceAccess,
getManageableWorkspaces,
getUserEntityPermissions,
getUsersWithPermissions,
getWorkspaceById,
getWorkspaceWithOwner,
hasAdminPermission,
hasWorkspaceAdminAccess,
workspaceExists,
} from '@/lib/workspaces/permissions/utils'
const mockDb = db as any
@@ -610,4 +614,209 @@ describe('Permission Utils', () => {
expect(result).toEqual([])
})
})
describe('getWorkspaceById', () => {
it.concurrent('should return workspace when it exists', async () => {
const chain = createMockChain([{ id: 'workspace123' }])
mockDb.select.mockReturnValue(chain)
const result = await getWorkspaceById('workspace123')
expect(result).toEqual({ id: 'workspace123' })
})
it.concurrent('should return null when workspace does not exist', async () => {
const chain = createMockChain([])
mockDb.select.mockReturnValue(chain)
const result = await getWorkspaceById('non-existent')
expect(result).toBeNull()
})
it.concurrent('should handle empty workspace ID', async () => {
const chain = createMockChain([])
mockDb.select.mockReturnValue(chain)
const result = await getWorkspaceById('')
expect(result).toBeNull()
})
})
describe('getWorkspaceWithOwner', () => {
it.concurrent('should return workspace with owner when it exists', async () => {
const chain = createMockChain([{ id: 'workspace123', ownerId: 'owner456' }])
mockDb.select.mockReturnValue(chain)
const result = await getWorkspaceWithOwner('workspace123')
expect(result).toEqual({ id: 'workspace123', ownerId: 'owner456' })
})
it.concurrent('should return null when workspace does not exist', async () => {
const chain = createMockChain([])
mockDb.select.mockReturnValue(chain)
const result = await getWorkspaceWithOwner('non-existent')
expect(result).toBeNull()
})
it.concurrent('should handle workspace with null owner ID', async () => {
const chain = createMockChain([{ id: 'workspace123', ownerId: null }])
mockDb.select.mockReturnValue(chain)
const result = await getWorkspaceWithOwner('workspace123')
expect(result).toEqual({ id: 'workspace123', ownerId: null })
})
})
describe('workspaceExists', () => {
it.concurrent('should return true when workspace exists', async () => {
const chain = createMockChain([{ id: 'workspace123' }])
mockDb.select.mockReturnValue(chain)
const result = await workspaceExists('workspace123')
expect(result).toBe(true)
})
it.concurrent('should return false when workspace does not exist', async () => {
const chain = createMockChain([])
mockDb.select.mockReturnValue(chain)
const result = await workspaceExists('non-existent')
expect(result).toBe(false)
})
it.concurrent('should handle empty workspace ID', async () => {
const chain = createMockChain([])
mockDb.select.mockReturnValue(chain)
const result = await workspaceExists('')
expect(result).toBe(false)
})
})
describe('checkWorkspaceAccess', () => {
it('should return exists=false when workspace does not exist', async () => {
const chain = createMockChain([])
mockDb.select.mockReturnValue(chain)
const result = await checkWorkspaceAccess('non-existent', 'user123')
expect(result).toEqual({
exists: false,
hasAccess: false,
canWrite: false,
workspace: null,
})
})
it('should return full access when user is workspace owner', async () => {
const chain = createMockChain([{ id: 'workspace123', ownerId: 'user123' }])
mockDb.select.mockReturnValue(chain)
const result = await checkWorkspaceAccess('workspace123', 'user123')
expect(result).toEqual({
exists: true,
hasAccess: true,
canWrite: true,
workspace: { id: 'workspace123', ownerId: 'user123' },
})
})
it('should return hasAccess=false when user has no permissions', async () => {
let callCount = 0
mockDb.select.mockImplementation(() => {
callCount++
if (callCount === 1) {
return createMockChain([{ id: 'workspace123', ownerId: 'other-user' }])
}
return createMockChain([]) // No permissions
})
const result = await checkWorkspaceAccess('workspace123', 'user123')
expect(result.exists).toBe(true)
expect(result.hasAccess).toBe(false)
expect(result.canWrite).toBe(false)
})
it('should return canWrite=true when user has admin permission', async () => {
let callCount = 0
mockDb.select.mockImplementation(() => {
callCount++
if (callCount === 1) {
return createMockChain([{ id: 'workspace123', ownerId: 'other-user' }])
}
return createMockChain([{ permissionType: 'admin' }])
})
const result = await checkWorkspaceAccess('workspace123', 'user123')
expect(result.exists).toBe(true)
expect(result.hasAccess).toBe(true)
expect(result.canWrite).toBe(true)
})
it('should return canWrite=true when user has write permission', async () => {
let callCount = 0
mockDb.select.mockImplementation(() => {
callCount++
if (callCount === 1) {
return createMockChain([{ id: 'workspace123', ownerId: 'other-user' }])
}
return createMockChain([{ permissionType: 'write' }])
})
const result = await checkWorkspaceAccess('workspace123', 'user123')
expect(result.exists).toBe(true)
expect(result.hasAccess).toBe(true)
expect(result.canWrite).toBe(true)
})
it('should return canWrite=false when user has read permission', async () => {
let callCount = 0
mockDb.select.mockImplementation(() => {
callCount++
if (callCount === 1) {
return createMockChain([{ id: 'workspace123', ownerId: 'other-user' }])
}
return createMockChain([{ permissionType: 'read' }])
})
const result = await checkWorkspaceAccess('workspace123', 'user123')
expect(result.exists).toBe(true)
expect(result.hasAccess).toBe(true)
expect(result.canWrite).toBe(false)
})
it('should handle empty user ID', async () => {
const chain = createMockChain([])
mockDb.select.mockReturnValue(chain)
const result = await checkWorkspaceAccess('workspace123', '')
expect(result.exists).toBe(false)
expect(result.hasAccess).toBe(false)
})
it('should handle empty workspace ID', async () => {
const chain = createMockChain([])
mockDb.select.mockReturnValue(chain)
const result = await checkWorkspaceAccess('', 'user123')
expect(result.exists).toBe(false)
expect(result.hasAccess).toBe(false)
})
})
})

View File

@@ -3,6 +3,112 @@ import { permissions, type permissionTypeEnum, user, workspace } from '@sim/db/s
import { and, eq } from 'drizzle-orm'
export type PermissionType = (typeof permissionTypeEnum.enumValues)[number]
export interface WorkspaceBasic {
id: string
}
export interface WorkspaceWithOwner {
id: string
ownerId: string
}
export interface WorkspaceAccess {
exists: boolean
hasAccess: boolean
canWrite: boolean
workspace: WorkspaceWithOwner | null
}
/**
* Check if a workspace exists
*
* @param workspaceId - The workspace ID to check
* @returns True if the workspace exists, false otherwise
*/
export async function workspaceExists(workspaceId: string): Promise<boolean> {
const [ws] = await db
.select({ id: workspace.id })
.from(workspace)
.where(eq(workspace.id, workspaceId))
.limit(1)
return !!ws
}
/**
* Get a workspace by ID for existence check
*
* @param workspaceId - The workspace ID to look up
* @returns The workspace if found, null otherwise
*/
export async function getWorkspaceById(workspaceId: string): Promise<WorkspaceBasic | null> {
const exists = await workspaceExists(workspaceId)
return exists ? { id: workspaceId } : null
}
/**
* Get a workspace with owner info by ID
*
* @param workspaceId - The workspace ID to look up
* @returns The workspace with owner info if found, null otherwise
*/
export async function getWorkspaceWithOwner(
workspaceId: string
): Promise<WorkspaceWithOwner | null> {
const [ws] = await db
.select({ id: workspace.id, ownerId: workspace.ownerId })
.from(workspace)
.where(eq(workspace.id, workspaceId))
.limit(1)
return ws || null
}
/**
* Check workspace access for a user
*
* Verifies the workspace exists and the user has access to it.
* Returns access level (read/write) based on ownership and permissions.
*
* @param workspaceId - The workspace ID to check
* @param userId - The user ID to check access for
* @returns WorkspaceAccess object with exists, hasAccess, canWrite, and workspace data
*/
export async function checkWorkspaceAccess(
workspaceId: string,
userId: string
): Promise<WorkspaceAccess> {
const ws = await getWorkspaceWithOwner(workspaceId)
if (!ws) {
return { exists: false, hasAccess: false, canWrite: false, workspace: null }
}
if (ws.ownerId === userId) {
return { exists: true, hasAccess: true, canWrite: true, workspace: ws }
}
const [permissionRow] = await db
.select({ permissionType: permissions.permissionType })
.from(permissions)
.where(
and(
eq(permissions.userId, userId),
eq(permissions.entityType, 'workspace'),
eq(permissions.entityId, workspaceId)
)
)
.limit(1)
if (!permissionRow) {
return { exists: true, hasAccess: false, canWrite: false, workspace: ws }
}
const canWrite =
permissionRow.permissionType === 'write' || permissionRow.permissionType === 'admin'
return { exists: true, hasAccess: true, canWrite, workspace: ws }
}
/**
* Get the highest permission level a user has for a specific entity
@@ -111,17 +217,13 @@ export async function hasWorkspaceAdminAccess(
userId: string,
workspaceId: string
): Promise<boolean> {
const workspaceResult = await db
.select({ ownerId: workspace.ownerId })
.from(workspace)
.where(eq(workspace.id, workspaceId))
.limit(1)
const ws = await getWorkspaceWithOwner(workspaceId)
if (workspaceResult.length === 0) {
if (!ws) {
return false
}
if (workspaceResult[0].ownerId === userId) {
if (ws.ownerId === userId) {
return true
}