diff --git a/src/management-system-v2/app/(dashboard)/[environmentId]/(automation)/executions/[processId]/process-deployment-view.tsx b/src/management-system-v2/app/(dashboard)/[environmentId]/(automation)/executions/[processId]/process-deployment-view.tsx index 86fea36dd..18ed6f770 100644 --- a/src/management-system-v2/app/(dashboard)/[environmentId]/(automation)/executions/[processId]/process-deployment-view.tsx +++ b/src/management-system-v2/app/(dashboard)/[environmentId]/(automation)/executions/[processId]/process-deployment-view.tsx @@ -33,6 +33,10 @@ import { DeployedProcessInfo } from '@/lib/engines/deployment'; import StartFormModal from './start-form-modal'; import useInstanceVariables from './use-instance-variables'; import { inlineScript, inlineUserTaskData } from '@proceed/user-task-helper'; +import { getGlobalVariablesForHTML } from '@/lib/engines/server-actions'; +import { useEnvironment } from '@/components/auth-can'; +import { useSession } from 'next-auth/react'; +import { isUserErrorResponse } from '@/lib/user-error'; export default function ProcessDeploymentView({ processId, @@ -56,6 +60,9 @@ export default function ProcessDeploymentView({ const canvasRef = useRef(null); const [infoPanelOpen, setInfoPanelOpen] = useState(false); + const { spaceId } = useEnvironment(); + const { data: session } = useSession(); + const { data: deploymentInfo, refetch, @@ -199,8 +206,21 @@ export default function ProcessDeploymentView({ .filter((variable) => variable.value !== undefined) .map((variable) => [variable.name, variable.value]), ); + + if (!session) throw new Error('Unknown user tries to start an instance!'); + + const globalVars = await getGlobalVariablesForHTML( + spaceId, + session.user.id, + startForm, + ); + startForm = inlineScript(startForm, '', '', variableDefinitions); - startForm = inlineUserTaskData(startForm, mappedVariables, []); + startForm = inlineUserTaskData( + startForm, + { ...mappedVariables, ...globalVars }, + [], + ); setStartForm(startForm); } else { diff --git a/src/management-system-v2/app/(dashboard)/[environmentId]/(automation)/executions/deployment-hook.ts b/src/management-system-v2/app/(dashboard)/[environmentId]/(automation)/executions/deployment-hook.ts index 0465c3446..935abc023 100644 --- a/src/management-system-v2/app/(dashboard)/[environmentId]/(automation)/executions/deployment-hook.ts +++ b/src/management-system-v2/app/(dashboard)/[environmentId]/(automation)/executions/deployment-hook.ts @@ -100,13 +100,14 @@ function useDeployment(definitionId: string, initialData?: DeployedProcessInfo) }); const startInstance = async (versionId: string, variables: { [key: string]: any } = {}) => { - if (engines?.length) - // TODO: in case of static deployment or different versions on different engines we will have - // to check if the engine can actually be used to start an instance - return await startInstanceOnMachine(definitionId, versionId, engines[0], variables, { - processInitiator: session?.user.id, - spaceIdOfProcessInitiator: space.spaceId, - }); + if (!engines?.length) return userError('No fitting engine found'); + + // TODO: in case of static deployment or different versions on different engines we will have + // to check if the engine can actually be used to start an instance + return await startInstanceOnMachine(definitionId, versionId, engines[0], variables, { + processInitiator: session?.user.id, + spaceIdOfProcessInitiator: space.spaceId, + }); }; const activeStates = ['PAUSED', 'RUNNING', 'READY', 'DEPLOYMENT-WAITING', 'WAITING']; @@ -170,7 +171,8 @@ function useDeployment(definitionId: string, initialData?: DeployedProcessInfo) } async function getStartForm(versionId: string) { - if (!engines) return; + if (!engines?.length) return userError('No fitting engine found'); + try { // TODO: in case of static deployment or different versions on different engines we will have // to check if the engine can actually be used to start an instance diff --git a/src/management-system-v2/lib/engines/deployment.ts b/src/management-system-v2/lib/engines/deployment.ts index b605c0ecb..1c92f011b 100644 --- a/src/management-system-v2/lib/engines/deployment.ts +++ b/src/management-system-v2/lib/engines/deployment.ts @@ -313,6 +313,9 @@ export type InstanceInfo = { adaptationLog: any[]; processVersion: string; userTasks: any[]; + managementSystemLocation?: string; + processInitiator?: string; + spaceIdOfProcessInitiator?: string; }; export type DeployedProcessInfo = { definitionId: string; diff --git a/src/management-system-v2/lib/engines/server-actions.ts b/src/management-system-v2/lib/engines/server-actions.ts index 33296ae09..81452f142 100644 --- a/src/management-system-v2/lib/engines/server-actions.ts +++ b/src/management-system-v2/lib/engines/server-actions.ts @@ -12,7 +12,7 @@ import { } from './deployment'; import { Engine, SpaceEngine } from './machines'; import { savedEnginesToEngines } from './saved-engines-helpers'; -import { getCurrentEnvironment } from '@/components/auth'; +import { getCurrentEnvironment, getCurrentUser } from '@/components/auth'; import { enableUseDB } from 'FeatureFlags'; import { getDbEngines, getDbEngineByAddress } from '@/lib/data/db/engines'; import { asyncFilter, asyncMap, asyncForEach } from '../helpers/javascriptHelpers'; @@ -34,6 +34,7 @@ import { getCorrectVariableState, getCorrectMilestoneState, inlineScript, + getGlobalVariables, } from '@proceed/user-task-helper'; import { ExtendedTaskListEntry, UserTask } from '../user-task-schema'; @@ -49,6 +50,7 @@ import { getProcessIds, getVariablesFromElementById } from '@proceed/bpmn-helper import { Variable } from '@proceed/bpmn-helper/src/getters'; import Ability from '../ability/abilityHelper'; import { getUserById } from '../data/db/iam/users'; +import { getDataObject, isErrorResponse } from '@/app/api/spaces/[spaceId]/data/helper'; export async function getCorrectTargetEngines( spaceId: string, @@ -299,9 +301,11 @@ export async function getAvailableTaskListEntries(spaceId: string, engines: Engi username?: string | null; firstName?: string | null; lastName?: string | null; - }[] = await asyncMap(task.actualOwner, async (owner) => { - return getUserById(owner, undefined, tx) || owner; - }); + }[] = ( + await asyncMap(task.actualOwner, async (owner) => { + return getUserById(owner, undefined, tx) || owner; + }) + ).filter(truthyFilter); return users.map((user) => typeof user === 'string' @@ -321,6 +325,38 @@ export async function getAvailableTaskListEntries(spaceId: string, engines: Engi } } +export async function getGlobalVariablesForHTML( + spaceId: string, + initiatorId: string, + html: string, +) { + return await getGlobalVariables(html, async (varPath) => { + let segments = varPath.split('.'); + + let userId: string | undefined; + + if (segments[0] === '@process-initiator') { + userId = initiatorId; + } else if (segments[0] === '@worker' || !segments[0].startsWith('@')) { + ({ userId } = await getCurrentUser()); + } else if (segments[0] !== '@organization') { + throw new UserFacingError( + `Invalid selector for global data access in user task html. (${segments[0]})`, + ); + } + + if (segments[0].startsWith('@')) segments = segments.slice(1); + + const result = await getDataObject(spaceId, segments.join('.'), userId); + + if (isErrorResponse(result)) { + throw new UserFacingError(await result.data.text()); + } + + return result.data?.value; + }); +} + export async function getTasklistEntryHTML( spaceId: string, userTaskId: string, @@ -348,6 +384,7 @@ export async function getTasklistEntryHTML( if (engine && (!html || !milestones || !initialVariables)) { const [taskId, instanceId, startTimeString] = userTaskId.split('|'); + const [definitionId] = instanceId.split('-_'); const startTime = parseInt(startTimeString); @@ -371,10 +408,11 @@ export async function getTasklistEntryHTML( initialVariables = getCorrectVariableState(userTask, instance); milestones = await getCorrectMilestoneState(version.bpmn, userTask, instance); - variableChanges = initialVariables; html = await getUserTaskFileFromMachine(engine, definitionId, filename); + variableChanges = initialVariables; + html = html.replace(/\/resources\/process[^"]*/g, (match) => { const path = match.split('/'); return `/api/private/${spaceId}/engine/resources/process/${definitionId}/images/${path.pop()}`; @@ -430,6 +468,23 @@ export async function getTasklistEntryHTML( if (!html) throw new Error('Failed to get the html for the user task'); if (!milestones) throw new Error('Failed to get the milestones for the user task'); + let globalVars: Record = {}; + + if (storedUserTask.instanceID) { + if (!engine) throw new Error('Cannot retrieve the instance initiator information.'); + const [definitionId] = storedUserTask.instanceID.split('-_'); + const deployment = await fetchDeployment(engine, definitionId); + const instance = deployment.instances.find( + (i) => i.processInstanceId === storedUserTask.instanceID, + ); + if (!instance) throw new Error('Unknown instance'); + if (!instance.processInitiator) throw new Error('Missing initiator information'); + + globalVars = await getGlobalVariablesForHTML(spaceId, instance.processInitiator, html); + } + + variableChanges = { ...variableChanges, ...globalVars }; + return inlineUserTaskData(html, mapResourceUrls(variableChanges), milestones); } catch (e) { const message = getErrorMessage(e);