Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 0 additions & 57 deletions apps/sim/app/api/workflows/[id]/deploy/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,18 @@ describe('Workflow Deployment API Route', () => {
beforeEach(() => {
vi.resetModules()

// Mock utils
vi.doMock('@/lib/utils', () => ({
generateApiKey: vi.fn().mockReturnValue('sim_testkeygenerated12345'),
}))

// Mock UUID generation
vi.doMock('uuid', () => ({
v4: vi.fn().mockReturnValue('mock-uuid-1234'),
}))

// Mock crypto for request ID
vi.stubGlobal('crypto', {
randomUUID: vi.fn().mockReturnValue('mock-request-id'),
})

// Mock logger
vi.doMock('@/lib/logs/console-logger', () => ({
createLogger: vi.fn().mockReturnValue({
debug: vi.fn(),
Expand All @@ -35,7 +31,6 @@ describe('Workflow Deployment API Route', () => {
}),
}))

// Mock the middleware to pass validation
vi.doMock('../../middleware', () => ({
validateWorkflowAccess: vi.fn().mockResolvedValue({
workflow: {
Expand All @@ -45,7 +40,6 @@ describe('Workflow Deployment API Route', () => {
}),
}))

// Mock the response utils
vi.doMock('../../utils', () => ({
createSuccessResponse: vi.fn().mockImplementation((data) => {
return new Response(JSON.stringify(data), {
Expand All @@ -70,7 +64,6 @@ describe('Workflow Deployment API Route', () => {
* Test GET deployment status
*/
it('should fetch deployment info successfully', async () => {
// Mock the database with proper workflow data
vi.doMock('@/db', () => ({
db: {
select: vi.fn().mockReturnValue({
Expand All @@ -89,25 +82,18 @@ describe('Workflow Deployment API Route', () => {
},
}))

// Create a mock request
const req = createMockRequest('GET')

// Create params similar to what Next.js would provide
const params = Promise.resolve({ id: 'workflow-id' })

// Import the handler after mocks are set up
const { GET } = await import('./route')

// Call the handler
const response = await GET(req, { params })

// Check response
expect(response.status).toBe(200)

// Parse the response body
const data = await response.json()

// Verify response structure
expect(data).toHaveProperty('isDeployed', false)
expect(data).toHaveProperty('apiKey', null)
expect(data).toHaveProperty('deployedAt', null)
Expand All @@ -118,7 +104,6 @@ describe('Workflow Deployment API Route', () => {
* This should generate a new API key
*/
it('should create new API key when deploying workflow for user with no API key', async () => {
// Mock DB for this test
const mockInsert = vi.fn().mockReturnValue({
values: vi.fn().mockReturnValue(undefined),
})
Expand Down Expand Up @@ -156,30 +141,22 @@ describe('Workflow Deployment API Route', () => {
},
}))

// Create a mock request
const req = createMockRequest('POST')

// Create params
const params = Promise.resolve({ id: 'workflow-id' })

// Import required modules after mocks are set up
const { POST } = await import('./route')

// Call the handler
const response = await POST(req, { params })

// Check response
expect(response.status).toBe(200)

// Parse the response body
const data = await response.json()

// Verify API key was generated
expect(data).toHaveProperty('apiKey', 'sim_testkeygenerated12345')
expect(data).toHaveProperty('isDeployed', true)
expect(data).toHaveProperty('deployedAt')

// Verify database calls
expect(mockInsert).toHaveBeenCalled()
expect(mockUpdate).toHaveBeenCalled()
})
Expand All @@ -189,7 +166,6 @@ describe('Workflow Deployment API Route', () => {
* This should use the existing API key
*/
it('should use existing API key when deploying workflow', async () => {
// Mock DB for this test
const mockInsert = vi.fn()

const mockUpdate = vi.fn().mockReturnValue({
Expand Down Expand Up @@ -229,29 +205,21 @@ describe('Workflow Deployment API Route', () => {
},
}))

// Create a mock request
const req = createMockRequest('POST')

// Create params
const params = Promise.resolve({ id: 'workflow-id' })

// Import required modules after mocks are set up
const { POST } = await import('./route')

// Call the handler
const response = await POST(req, { params })

// Check response
expect(response.status).toBe(200)

// Parse the response body
const data = await response.json()

// Verify existing API key was used
expect(data).toHaveProperty('apiKey', 'sim_existingtestapikey12345')
expect(data).toHaveProperty('isDeployed', true)

// Verify database calls - should NOT have inserted a new API key
expect(mockInsert).not.toHaveBeenCalled()
expect(mockUpdate).toHaveBeenCalled()
})
Expand All @@ -260,7 +228,6 @@ describe('Workflow Deployment API Route', () => {
* Test DELETE undeployment
*/
it('should undeploy workflow successfully', async () => {
// Mock the DB for this test
const mockUpdate = vi.fn().mockReturnValue({
set: vi.fn().mockReturnValue({
where: vi.fn().mockResolvedValue([{ id: 'workflow-id' }]),
Expand All @@ -273,38 +240,29 @@ describe('Workflow Deployment API Route', () => {
},
}))

// Create a mock request
const req = createMockRequest('DELETE')

// Create params
const params = Promise.resolve({ id: 'workflow-id' })

// Import the handler after mocks are set up
const { DELETE } = await import('./route')

// Call the handler
const response = await DELETE(req, { params })

// Check response
expect(response.status).toBe(200)

// Parse the response body
const data = await response.json()

// Verify response structure
expect(data).toHaveProperty('isDeployed', false)
expect(data).toHaveProperty('deployedAt', null)
expect(data).toHaveProperty('apiKey', null)

// Verify database calls
expect(mockUpdate).toHaveBeenCalled()
})

/**
* Test error handling
*/
it('should handle errors when workflow is not found', async () => {
// Mock middleware to simulate an error
vi.doMock('../../middleware', () => ({
validateWorkflowAccess: vi.fn().mockResolvedValue({
error: {
Expand All @@ -314,33 +272,25 @@ describe('Workflow Deployment API Route', () => {
}),
}))

// Create a mock request
const req = createMockRequest('POST')

// Create params with an invalid ID
const params = Promise.resolve({ id: 'invalid-id' })

// Import the handler after mocks are set up
const { POST } = await import('./route')

// Call the handler
const response = await POST(req, { params })

// Check response
expect(response.status).toBe(404)

// Parse the response body
const data = await response.json()

// Verify error message
expect(data).toHaveProperty('error', 'Workflow not found')
})

/**
* Test unauthorized access
*/
it('should handle unauthorized access to workflow', async () => {
// Mock middleware to simulate unauthorized access
vi.doMock('../../middleware', () => ({
validateWorkflowAccess: vi.fn().mockResolvedValue({
error: {
Expand All @@ -350,25 +300,18 @@ describe('Workflow Deployment API Route', () => {
}),
}))

// Create a mock request
const req = createMockRequest('POST')

// Create params
const params = Promise.resolve({ id: 'workflow-id' })

// Import the handler after mocks are set up
const { POST } = await import('./route')

// Call the handler
const response = await POST(req, { params })

// Check response
expect(response.status).toBe(403)

// Parse the response body
const data = await response.json()

// Verify error message
expect(data).toHaveProperty('error', 'Unauthorized access')
})
})
1 change: 0 additions & 1 deletion apps/sim/app/api/workflows/[id]/variables/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import type { Variable } from '@/stores/panel/variables/types'

const logger = createLogger('WorkflowVariablesAPI')

// Schema for workflow variables updates
const VariablesSchema = z.object({
variables: z.array(
z.object({
Expand Down
29 changes: 2 additions & 27 deletions apps/sim/app/api/workspaces/[id]/permissions/route.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { getUsersWithPermissions } from '@/lib/permissions/utils'
import { db } from '@/db'
import { permissions, type permissionTypeEnum, user, workspaceMember } from '@/db/schema'
import { permissions, type permissionTypeEnum, workspaceMember } from '@/db/schema'

// Extract the enum type from Drizzle schema
type PermissionType = (typeof permissionTypeEnum.enumValues)[number]

interface UpdatePermissionsRequest {
Expand All @@ -14,31 +14,6 @@ interface UpdatePermissionsRequest {
}>
}

// Helper function to fetch users with permissions for a workspace
async function getUsersWithPermissions(workspaceId: string) {
const usersWithPermissions = await db
.select({
userId: user.id,
email: user.email,
name: user.name,
image: user.image,
permissionType: permissions.permissionType,
})
.from(permissions)
.innerJoin(user, eq(permissions.userId, user.id))
.where(and(eq(permissions.entityType, 'workspace'), eq(permissions.entityId, workspaceId)))
.orderBy(user.email)

// Since each user has only one permission, we can use the results directly
return usersWithPermissions.map((row) => ({
userId: row.userId,
email: row.email,
name: row.name,
image: row.image,
permissionType: row.permissionType,
}))
}

/**
* GET /api/workspaces/[id]/permissions
*
Expand Down
1 change: 0 additions & 1 deletion apps/sim/app/api/workspaces/invitations/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export const dynamic = 'force-dynamic'
const logger = createLogger('WorkspaceInvitationsAPI')
const resend = env.RESEND_API_KEY ? new Resend(env.RESEND_API_KEY) : null

// Define the permission type
type PermissionType = (typeof permissionTypeEnum.enumValues)[number]

// Get all invitations for the user's workspaces
Expand Down
41 changes: 1 addition & 40 deletions apps/sim/app/api/workspaces/members/route.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,12 @@
import { and, eq } from 'drizzle-orm'
import { NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { hasAdminPermission } from '@/lib/permissions/utils'
import { db } from '@/db'
import { permissions, type permissionTypeEnum, user, workspaceMember } from '@/db/schema'

// Extract the enum type from Drizzle schema
type PermissionType = (typeof permissionTypeEnum.enumValues)[number]

/**
* Helper function to check if a user has admin permission for a workspace
*/
async function hasAdminPermission(userId: string, workspaceId: string): Promise<boolean> {
const result = await db
.select()
.from(permissions)
.where(
and(
eq(permissions.userId, userId),
eq(permissions.entityType, 'workspace'),
eq(permissions.entityId, workspaceId),
eq(permissions.permissionType, 'admin')
)
)
.limit(1)

return result.length > 0
}

/**
* Helper function to create default permissions for a new member
*/
async function createMemberPermissions(
userId: string,
workspaceId: string,
memberPermission: PermissionType = 'read'
): Promise<void> {
await db.insert(permissions).values({
id: crypto.randomUUID(),
userId,
entityType: 'workspace' as const,
entityId: workspaceId,
permissionType: memberPermission,
createdAt: new Date(),
updatedAt: new Date(),
})
}

// Add a member to a workspace
export async function POST(req: Request) {
const session = await getSession()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export function DeploymentControls({

const getTooltipText = () => {
if (!canDeploy) {
return 'Admin permissions required to deploy workflows as API'
return 'Admin permissions required to deploy workflows'
}
if (isDeploying) {
return 'Deploying...'
Expand Down
Loading