dify/web/features/deployments/create-guide/state/submission.ts
Stephen Zhou a84c2d36a3
style: format with vp fmt (#38803)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-12 15:57:46 +00:00

222 lines
8.1 KiB
TypeScript

'use client'
import type { DeployRequest } from '@dify/contracts/enterprise/types.gen'
import { atom } from 'jotai'
import { atomWithMutation } from 'jotai-tanstack-query'
import { selectedDeploymentRuntimeCredentials } from '@/features/deployments/shared/components/runtime-credential-bindings-utils'
import { encodeDslContent, isWorkflowDsl } from '@/features/deployments/shared/domain/dsl'
import { unsupportedDslNodeError } from '@/features/deployments/shared/domain/error'
import { createDeploymentIdempotencyKey } from '@/features/deployments/shared/domain/idempotency'
import { consoleQuery } from '@/service/client'
import { environmentMatchesIdentifier } from './environment'
import {
effectiveMethodAtom,
envVarValuesAtom,
instanceDescriptionAtom,
instanceNameAtom,
isCreatingDeploymentAtom,
isCreatingReleaseOnlyAtom,
isSubmittingDeploymentGuideAtom,
releaseDescriptionAtom,
releaseNameAtom,
selectedEnvironmentIdAtom,
submissionUnsupportedDslNodesAtom,
} from './primitives'
import { deployableEnvironmentsQueryAtom, deploymentOptionsDataAtom } from './queries'
import { submittedReleaseReadyAtom } from './release'
import { dslContentAtom, effectiveSelectedAppAtom } from './source'
import {
canDeployAtom,
canSkipDeploymentAtom,
deployableEnvironmentsAtom,
deploymentTargetBindingSelectionsAtom,
deploymentTargetBindingSlotsAtom,
deploymentTargetEnvVarSlotsAtom,
requiredBindingsReadyAtom,
requiredEnvVarsReadyAtom,
} from './target'
import { envVarInput } from './utils'
const createAppInstanceMutationAtom = atomWithMutation(() =>
consoleQuery.enterprise.appInstanceService.createAppInstance.mutationOptions(),
)
const createReleaseMutationAtom = atomWithMutation(() =>
consoleQuery.enterprise.releaseService.createRelease.mutationOptions(),
)
const createInitialDeploymentMutationAtom = atomWithMutation(() =>
consoleQuery.enterprise.deploymentService.deploy.mutationOptions(),
)
export class CreateDeploymentGuideSubmissionBlockedError extends Error {
reason: 'unsupportedDslMode' | 'deployFailed'
constructor(reason: 'unsupportedDslMode' | 'deployFailed') {
super(reason)
this.reason = reason
this.name = 'CreateDeploymentGuideSubmissionBlockedError'
}
}
export const createDeploymentGuideSubmissionAtom = atom(
null,
async (
get,
set,
{
deployToEnvironment,
}: {
deployToEnvironment: boolean
},
) => {
const method = get(effectiveMethodAtom)
const dslContent = get(dslContentAtom)
const submittedInstanceName = get(instanceNameAtom).trim()
const submittedReleaseName = get(releaseNameAtom).trim()
const submittedReleaseDescription = get(releaseDescriptionAtom).trim()
if (get(isSubmittingDeploymentGuideAtom) || !get(submittedReleaseReadyAtom)) return undefined
const effectiveSelectedApp = get(effectiveSelectedAppAtom)
const deployableEnvironmentsQuery = get(deployableEnvironmentsQueryAtom)
const deploymentOptions = get(deploymentOptionsDataAtom)?.options
const envVarSlots = get(deploymentTargetEnvVarSlotsAtom)
const envVarValues = get(envVarValuesAtom)
const bindingSlots = get(deploymentTargetBindingSlotsAtom)
const bindingSelections = get(deploymentTargetBindingSelectionsAtom)
const selectedEnvironmentId = get(selectedEnvironmentIdAtom)
const effectiveSelectedEnvironmentId =
selectedEnvironmentId || get(deployableEnvironmentsAtom)[0]?.id
const selectedEnvironment = effectiveSelectedEnvironmentId
? get(deployableEnvironmentsAtom).find((env) =>
environmentMatchesIdentifier(env, effectiveSelectedEnvironmentId),
)
: undefined
if (deployToEnvironment && !selectedEnvironment && !selectedEnvironmentId.trim())
return undefined
if (method === 'bindApp' && !effectiveSelectedApp?.id) return undefined
if (method === 'importDsl' && !dslContent.trim()) return undefined
if (method === 'importDsl' && !isWorkflowDsl(dslContent))
throw new CreateDeploymentGuideSubmissionBlockedError('unsupportedDslMode')
set(submissionUnsupportedDslNodesAtom, [])
try {
if (!deployToEnvironment) {
if (!get(canSkipDeploymentAtom)) return undefined
set(isCreatingReleaseOnlyAtom, true)
try {
const createdAppInstance = await get(createAppInstanceMutationAtom).mutateAsync({
body: {
displayName: submittedInstanceName,
description: get(instanceDescriptionAtom).trim() || undefined,
},
})
const appInstanceId = createdAppInstance.appInstance.id
if (method === 'importDsl') {
await get(createReleaseMutationAtom).mutateAsync({
body: {
appInstanceId,
dsl: encodeDslContent(dslContent),
displayName: submittedReleaseName,
description: submittedReleaseDescription || undefined,
createAppInstance: false,
},
})
return appInstanceId
}
if (!effectiveSelectedApp?.id) return undefined
await get(createReleaseMutationAtom).mutateAsync({
body: {
appInstanceId,
sourceAppId: effectiveSelectedApp.id,
displayName: submittedReleaseName,
description: submittedReleaseDescription || undefined,
createAppInstance: false,
},
})
return appInstanceId
} finally {
set(isCreatingReleaseOnlyAtom, false)
}
}
if (!get(canDeployAtom)) return undefined
set(isCreatingDeploymentAtom, true)
try {
const selectedEnvironmentIdentifier = selectedEnvironmentId.trim()
const freshSelectedEnvironment =
selectedEnvironment ||
(selectedEnvironmentIdentifier
? (await deployableEnvironmentsQuery.refetch()).data?.environments.find((environment) =>
environmentMatchesIdentifier(environment, selectedEnvironmentIdentifier),
)
: undefined)
const targetEnvironmentId = freshSelectedEnvironment?.id
if (!targetEnvironmentId)
throw new CreateDeploymentGuideSubmissionBlockedError('deployFailed')
if (!get(requiredBindingsReadyAtom)) throw new Error('Missing required deployment binding.')
if (!get(requiredEnvVarsReadyAtom))
throw new Error('Missing required deployment environment variable.')
const envVars = envVarSlots.flatMap((slot) => envVarInput(slot, envVarValues[slot.key]))
const commonDeploymentRequest = {
newAppInstance: {
displayName: submittedInstanceName,
description: get(instanceDescriptionAtom).trim() || undefined,
},
environmentId: targetEnvironmentId,
releaseName: submittedReleaseName,
releaseDescription: submittedReleaseDescription || undefined,
credentials: selectedDeploymentRuntimeCredentials(bindingSlots, bindingSelections),
envVars,
idempotencyKey: createDeploymentIdempotencyKey(),
expectedDslDigest: deploymentOptions?.dslDigest,
} satisfies Omit<DeployRequest, 'dsl' | 'sourceAppId'>
const deploymentRequest =
method === 'importDsl'
? {
...commonDeploymentRequest,
dsl: encodeDslContent(dslContent),
}
: effectiveSelectedApp?.id
? {
...commonDeploymentRequest,
sourceAppId: effectiveSelectedApp.id,
}
: undefined
if (!deploymentRequest) return undefined
const response = await get(createInitialDeploymentMutationAtom).mutateAsync({
body: deploymentRequest,
})
return response.appInstance.id
} finally {
set(isCreatingDeploymentAtom, false)
}
} catch (error) {
const unsupportedError = await unsupportedDslNodeError(error)
if (unsupportedError?.nodes.length) {
set(submissionUnsupportedDslNodesAtom, unsupportedError.nodes)
return undefined
}
throw error
}
},
)