Skip to content

Commit 448b493

Browse files
fix(workflows): finish delegated application migration
1 parent f138f6d commit 448b493

39 files changed

Lines changed: 1217 additions & 953 deletions

apps/sim/app/api/v2/workflows/[id]/deploy/route.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const mocks = vi.hoisted(() => ({
99
}))
1010

1111
vi.mock('@/lib/api/server/routes', () => ({
12+
createInternalSessionOrExecutorAuth: vi.fn(() => ({ kind: 'internal-workflow' })),
1213
defineV2JsonRoute: mocks.defineRoute,
1314
v2ApiKeyAuth: { kind: 'v2-api-key' },
1415
v2RateLimits: { publicApi: { kind: 'public-api' } },

apps/sim/app/api/v2/workflows/[id]/rollback/route.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const mocks = vi.hoisted(() => ({
88
}))
99

1010
vi.mock('@/lib/api/server/routes', () => ({
11+
createInternalSessionOrExecutorAuth: vi.fn(() => ({ kind: 'internal-workflow' })),
1112
defineV2JsonRoute: mocks.defineRoute,
1213
v2ApiKeyAuth: { kind: 'v2-api-key' },
1314
v2RateLimits: { publicApi: { kind: 'public-api' } },

apps/sim/app/api/workflows/[id]/deploy/route.ts

Lines changed: 50 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
2-
import { db, workflow } from '@sim/db'
31
import { createLogger } from '@sim/logger'
4-
import { assertWorkflowMutable, WorkflowLockedError } from '@sim/platform-authz/workflow'
52
import { getErrorMessage } from '@sim/utils/errors'
6-
import { eq } from 'drizzle-orm'
73
import type { NextRequest } from 'next/server'
84
import { updatePublicApiContract } from '@/lib/api/contracts/deployments'
95
import { parseRequest } from '@/lib/api/server'
@@ -12,18 +8,13 @@ import { asOrchestrationError, statusForOrchestrationError } from '@/lib/core/or
128
import { generateRequestId } from '@/lib/core/utils/request'
139
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
1410
import { captureServerEvent } from '@/lib/posthog/server'
15-
import { deployWorkflow, undeployWorkflow } from '@/lib/workflows/application/deployments'
16-
import { getWorkflowDeploymentSummary } from '@/lib/workflows/orchestration'
17-
import { validateWorkflowPermissions } from '@/lib/workflows/utils'
1811
import {
19-
checkNeedsRedeployment,
20-
createErrorResponse,
21-
createSuccessResponse,
22-
} from '@/app/api/workflows/utils'
23-
import {
24-
PublicApiNotAllowedError,
25-
validatePublicApiAllowed,
26-
} from '@/ee/access-control/utils/permission-check'
12+
deployWorkflow,
13+
readWorkflowDeploymentStatus,
14+
undeployWorkflow,
15+
} from '@/lib/workflows/application/deployments'
16+
import { updateWorkflowPublicApi } from '@/lib/workflows/application/update-workflow-deployment-settings'
17+
import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils'
2718

2819
const logger = createLogger('WorkflowDeployAPI')
2920

@@ -37,14 +28,13 @@ export const GET = withRouteHandler(
3728
const { id } = await params
3829

3930
try {
40-
const { error, workflow: workflowData } = await validateWorkflowPermissions(
41-
id,
42-
requestId,
43-
'read'
44-
)
45-
if (error) {
46-
return createErrorResponse(error.message, error.status)
47-
}
31+
const principal = await internalSessionAuth.authenticate()
32+
const result = await readWorkflowDeploymentStatus.execute({
33+
principal,
34+
input: { workflowId: id },
35+
request,
36+
})
37+
const workflowData = result.workflow
4838

4939
/**
5040
* A workflow is deployed only when an active version snapshot exists —
@@ -53,8 +43,7 @@ export const GET = withRouteHandler(
5343
* disagrees with the version table the workflow cannot actually serve
5444
* traffic, so reporting it as live would be untruthful.
5545
*/
56-
const deploymentSummary = await getWorkflowDeploymentSummary(id)
57-
const isDeployed = deploymentSummary.activeDeployment !== null
46+
const isDeployed = result.isDeployed
5847

5948
if (!isDeployed) {
6049
logger.info(`[${requestId}] Workflow is not deployed: ${id}`)
@@ -64,18 +53,12 @@ export const GET = withRouteHandler(
6453
apiKey: null,
6554
needsRedeployment: false,
6655
isPublicApi: workflowData.isPublicApi ?? false,
67-
activeDeployment: deploymentSummary.activeDeployment,
68-
latestDeploymentAttempt: deploymentSummary.latestDeploymentAttempt,
69-
warnings: deploymentSummary.warnings,
56+
activeDeployment: result.activeDeployment,
57+
latestDeploymentAttempt: result.latestDeploymentAttempt,
58+
warnings: result.warnings,
7059
})
7160
}
7261

73-
const attemptStatus = deploymentSummary.latestDeploymentAttempt?.status
74-
const needsRedeployment =
75-
attemptStatus === 'preparing' || attemptStatus === 'activating'
76-
? false
77-
: await checkNeedsRedeployment(id)
78-
7962
logger.info(`[${requestId}] Successfully retrieved deployment info: ${id}`)
8063

8164
const responseApiKeyInfo = workflowData.workspaceId
@@ -85,14 +68,24 @@ export const GET = withRouteHandler(
8568
return createSuccessResponse({
8669
apiKey: responseApiKeyInfo,
8770
isDeployed,
88-
deployedAt: deploymentSummary.activeDeployment?.deployedAt ?? workflowData.deployedAt,
89-
needsRedeployment,
71+
deployedAt: result.activeDeployment?.deployedAt ?? workflowData.deployedAt,
72+
needsRedeployment: result.needsRedeployment,
9073
isPublicApi: workflowData.isPublicApi ?? false,
91-
activeDeployment: deploymentSummary.activeDeployment,
92-
latestDeploymentAttempt: deploymentSummary.latestDeploymentAttempt,
93-
warnings: deploymentSummary.warnings,
74+
activeDeployment: result.activeDeployment,
75+
latestDeploymentAttempt: result.latestDeploymentAttempt,
76+
warnings: result.warnings,
9477
})
9578
} catch (error: unknown) {
79+
if (error instanceof InternalUnauthenticatedError) {
80+
return createErrorResponse(error.message, 401)
81+
}
82+
const orchestrationError = asOrchestrationError(error)
83+
if (orchestrationError) {
84+
return createErrorResponse(
85+
orchestrationError.message,
86+
statusForOrchestrationError(orchestrationError.code)
87+
)
88+
}
9689
logger.error(`[${requestId}] Error fetching deployment info: ${id}`, {
9790
error: getErrorMessage(error, 'Unknown error'),
9891
})
@@ -152,6 +145,7 @@ export const PATCH = withRouteHandler(
152145
const requestId = generateRequestId()
153146

154147
try {
148+
const principal = await internalSessionAuth.authenticate()
155149
const parsed = await parseRequest(updatePublicApiContract, request, context, {
156150
validationErrorResponse: () =>
157151
createErrorResponse('Invalid request body: isPublicApi must be a boolean', 400),
@@ -161,56 +155,32 @@ export const PATCH = withRouteHandler(
161155
const { id } = parsed.data.params
162156
const { isPublicApi } = parsed.data.body
163157

164-
const {
165-
error,
166-
session,
167-
workflow: workflowData,
168-
} = await validateWorkflowPermissions(id, requestId, 'admin')
169-
if (error) {
170-
return createErrorResponse(error.message, error.status)
171-
}
172-
await assertWorkflowMutable(id)
173-
174-
if (isPublicApi) {
175-
try {
176-
await validatePublicApiAllowed(session?.user?.id, workflowData?.workspaceId ?? undefined)
177-
} catch (err) {
178-
if (err instanceof PublicApiNotAllowedError) {
179-
return createErrorResponse('Public API access is disabled', 403)
180-
}
181-
throw err
182-
}
183-
}
184-
185-
await db.update(workflow).set({ isPublicApi }).where(eq(workflow.id, id))
186-
187-
logger.info(`[${requestId}] Updated isPublicApi for workflow ${id} to ${isPublicApi}`)
188-
189-
const wsId = workflowData?.workspaceId
190-
191-
recordAudit({
192-
workspaceId: wsId ?? null,
193-
actorId: session!.user.id,
194-
action: AuditAction.WORKFLOW_PUBLIC_API_TOGGLED,
195-
resourceType: AuditResourceType.WORKFLOW,
196-
resourceId: id,
197-
resourceName: workflowData?.name ?? undefined,
198-
description: `${isPublicApi ? 'Enabled' : 'Disabled'} public API for workflow "${workflowData?.name ?? id}"`,
199-
metadata: { isPublicApi },
158+
const result = await updateWorkflowPublicApi.execute({
159+
principal,
160+
input: { workflowId: id, isPublicApi },
200161
request,
201162
})
202163

164+
logger.info(`[${requestId}] Updated isPublicApi for workflow ${id} to ${isPublicApi}`)
165+
203166
captureServerEvent(
204-
session!.user.id,
167+
principal.userId,
205168
'workflow_public_api_toggled',
206-
{ workflow_id: id, workspace_id: wsId ?? '', is_public: isPublicApi },
207-
wsId ? { groups: { workspace: wsId } } : undefined
169+
{ workflow_id: id, workspace_id: result.workspaceId, is_public: isPublicApi },
170+
{ groups: { workspace: result.workspaceId } }
208171
)
209172

210173
return createSuccessResponse({ isPublicApi })
211174
} catch (error: unknown) {
212-
if (error instanceof WorkflowLockedError) {
213-
return createErrorResponse(error.message, error.status)
175+
if (error instanceof InternalUnauthenticatedError) {
176+
return createErrorResponse(error.message, 401)
177+
}
178+
const orchestrationError = asOrchestrationError(error)
179+
if (orchestrationError) {
180+
return createErrorResponse(
181+
orchestrationError.message,
182+
statusForOrchestrationError(orchestrationError.code)
183+
)
214184
}
215185
logger.error(`[${requestId}] Error updating deployment settings`, {
216186
error: getErrorMessage(error, 'Unknown error'),

0 commit comments

Comments
 (0)