Skip to content

Commit 3cb08c6

Browse files
authored
Merge pull request #746 from PROCEED-Labs/persistent-deployment-data
Persistent Executions Data: Initial Commit
2 parents b87427b + 3645693 commit 3cb08c6

20 files changed

Lines changed: 600 additions & 241 deletions

File tree

src/engine/e2e_tests/process/deployment/deployment.e2e.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ async function startInstance(definitionId, version, engineName, variables = {},
6666
await request
6767
.post(`:${engine.port}/process/${definitionId}/versions/${version}/instance`)
6868
.send({ variables, extras })
69-
).body.instanceId;
69+
).body.processInstanceId;
7070
}
7171

7272
async function getInstanceInformation(definitionId, instanceId, engineName) {

src/engine/e2e_tests/process/processEndpoint.e2e.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ describe('Test process endpoints', () => {
234234
expect(getResponse.body).toStrictEqual([]);
235235
const postResponse = await request.post('/process/definitionId/versions/123/instance');
236236
expect(postResponse.status).toBe(201);
237-
({ instanceId } = postResponse.body);
237+
({ processInstanceId: instanceId } = postResponse.body);
238238
// allow everything to start correctly (the user task should have completely started)
239239
await new Promise((res) => setTimeout(res, 500));
240240
});

src/engine/universal/core/src/__tests__/management.test.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,8 @@ describe('Management', () => {
151151
jest.spyOn(Engine.prototype, 'deployProcessVersion');
152152
jest.spyOn(Engine.prototype, 'startProcessVersion');
153153
distribution.db.isProcessVersionValid.mockResolvedValue(true);
154-
const instanceId = await management.createInstance('0', '123', {});
155-
expect(management.getEngineWithID(instanceId)).toBeInstanceOf(Engine);
154+
const instance = await management.createInstance('0', '123', {});
155+
expect(management.getEngineWithID(instance.processInstanceId)).toBeInstanceOf(Engine);
156156
expect(Engine.prototype.deployProcessVersion).toHaveBeenCalledWith('0', '123', true);
157157
expect(Engine.prototype.startProcessVersion).toHaveBeenCalledWith(
158158
'123',
@@ -166,9 +166,11 @@ describe('Management', () => {
166166
it('reuses an existing ProceedEngine instance when there is one for the given definitionsId to start an instance', async () => {
167167
jest.spyOn(Engine.prototype, 'deployProcessVersion');
168168
jest.spyOn(Engine.prototype, 'startProcessVersion');
169-
const firstInstanceId = await management.createInstance('0', '123', {});
169+
const firstInstance = await management.createInstance('0', '123', {});
170+
const firstInstanceId = firstInstance.processInstanceId;
170171

171-
const secondInstanceId = await management.createInstance('0', '123', {});
172+
const secondInstance = await management.createInstance('0', '123', {});
173+
const secondInstanceId = secondInstance.processInstanceId;
172174

173175
const firstEngine = management.getEngineWithID(firstInstanceId);
174176
expect(management.getEngineWithID(firstInstanceId)).toBe(

src/engine/universal/core/src/engine/engine.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ class Engine {
291291
this.originalInstanceState = instance;
292292
this.instanceEventHandlers = {
293293
onStarted: (newInstance) => {
294-
resolver(newInstance.id);
294+
resolver(this.getInstanceInformation(newInstance.id));
295295
// make sure to keep the information from the original instance on the recreated instance
296296
if (instance && instance.callingInstance) {
297297
newInstance.callingInstance = instance.callingInstance;

src/engine/universal/core/src/management.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ const Management = {
510510
};
511511
});
512512

513-
const instanceId = await engine.startProcessVersion(
513+
const recoveredInstance = await engine.startProcessVersion(
514514
processVersion,
515515
importedInstance.variables,
516516
importedInstance,
@@ -526,7 +526,7 @@ const Management = {
526526
// if the instance was in the process of being paused => make sure that it is paused again
527527
// (will lead to it being paused directly since no tasks have started yet)
528528
if (importedInstance.instanceState[0] === 'PAUSING') {
529-
await engine.pauseInstance(instanceId);
529+
await engine.pauseInstance(recoveredInstance.processInstanceId);
530530
}
531531

532532
// allow waiting instances to be started (and give information about tokens being interrupted which is needed to check if called instances should run)

src/engine/universal/distribution/src/routes/ProcessInstanceRoutes.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ module.exports = (path, management) => {
4545
}
4646
}
4747

48-
const instanceId = await management.createInstance(
48+
const instance = await management.createInstance(
4949
definitionId,
5050
version,
5151
variables,
@@ -57,7 +57,7 @@ module.exports = (path, management) => {
5757
},
5858
);
5959

60-
if (!instanceId) {
60+
if (!instance) {
6161
throw new APIError(
6262
406,
6363
`Engine not allowed to start the instance for the process (id: ${definitionId}).`,
@@ -70,7 +70,7 @@ module.exports = (path, management) => {
7070
return {
7171
statusCode: 201,
7272
mimeType: 'application/json',
73-
response: JSON.stringify({ instanceId }),
73+
response: JSON.stringify(instance),
7474
};
7575
});
7676

src/management-system-v2/app/(dashboard)/[environmentId]/(automation)/executions/[processId]/process-deployment-view.tsx

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ import { useEnvironment } from '@/components/auth-can';
4444

4545
import { GrDocumentUser } from 'react-icons/gr';
4646
import { handleOpenDocumentation } from '../../../processes/processes-helper';
47+
import {
48+
getProcessStartForm,
49+
pauseInstance,
50+
resumeInstance,
51+
startInstance,
52+
stopInstance,
53+
} from '@/lib/executions/instance-server-actions';
4754

4855
export default function ProcessDeploymentView({
4956
processId,
@@ -76,15 +83,7 @@ export default function ProcessDeploymentView({
7683
const canvasRef = useRef<BPMNCanvasRef>(null);
7784
const [infoPanelOpen, setInfoPanelOpen] = useState(false);
7885

79-
const {
80-
data: deploymentInfo,
81-
refetch,
82-
startInstance,
83-
resumeInstance,
84-
pauseInstance,
85-
stopInstance,
86-
getStartForm,
87-
} = useDeployment(processId, initialDeploymentInfo);
86+
const { data: deploymentInfo, refetch } = useDeployment(processId, initialDeploymentInfo);
8887

8988
const {
9089
selectedVersion,
@@ -341,7 +340,7 @@ export default function ProcessDeploymentView({
341340
const latestDeployment = getLatestDeployment(deploymentInfo);
342341
const versionId = latestDeployment.versionId;
343342

344-
let startForm = await getStartForm(versionId);
343+
let startForm = await getProcessStartForm(spaceId, processId, versionId);
345344

346345
if (typeof startForm !== 'string') return startForm;
347346

@@ -370,7 +369,7 @@ export default function ProcessDeploymentView({
370369

371370
setStartForm(startForm);
372371
} else {
373-
return startInstance(versionId);
372+
return startInstance(spaceId, processId, versionId);
374373
}
375374
},
376375
onSuccess: async (instanceId) => {
@@ -442,7 +441,12 @@ export default function ProcessDeploymentView({
442441
onClick={async () => {
443442
setResumingInstance(true);
444443
await wrapServerCall({
445-
fn: () => resumeInstance(selectedInstance.processInstanceId),
444+
fn: () =>
445+
resumeInstance(
446+
spaceId,
447+
processId,
448+
selectedInstance.processInstanceId,
449+
),
446450
onSuccess: async () => await refetch(),
447451
});
448452
setResumingInstance(false);
@@ -460,7 +464,8 @@ export default function ProcessDeploymentView({
460464
onClick={async () => {
461465
setPausingInstance(true);
462466
await wrapServerCall({
463-
fn: async () => pauseInstance(selectedInstance.processInstanceId),
467+
fn: async () =>
468+
pauseInstance(spaceId, processId, selectedInstance.processInstanceId),
464469
onSuccess: async () => await refetch(),
465470
});
466471
setPausingInstance(false);
@@ -478,7 +483,8 @@ export default function ProcessDeploymentView({
478483
onClick={async () => {
479484
setStoppingInstance(true);
480485
await wrapServerCall({
481-
fn: async () => stopInstance(selectedInstance.processInstanceId),
486+
fn: async () =>
487+
stopInstance(spaceId, processId, selectedInstance.processInstanceId),
482488
onSuccess: async () => await refetch(),
483489
});
484490
setStoppingInstance(false);
@@ -547,7 +553,7 @@ export default function ProcessDeploymentView({
547553

548554
// start the instance with the initial variable values from the start form
549555
await wrapServerCall({
550-
fn: () => startInstance(versionId, mappedVariables),
556+
fn: () => startInstance(spaceId, processId, versionId, mappedVariables),
551557

552558
onSuccess: async (instanceId) => {
553559
await refetch();
Lines changed: 3 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,12 @@
1-
import { useEnvironment, useSession } from '@/components/auth-can';
1+
import { useEnvironment } from '@/components/auth-can';
22
import {
33
DeployedProcessInfo,
44
InstanceInfo,
55
VersionInfo,
66
getDeployments,
77
} from '@/lib/engines/deployment';
8-
import {
9-
pauseInstanceOnMachine,
10-
resumeInstanceOnMachine,
11-
startInstanceOnMachine,
12-
stopInstanceOnMachine,
13-
} from '@/lib/engines/instances';
14-
import { Engine } from '@/lib/engines/types';
15-
import { getStartFormFromMachine } from '@/lib/engines/tasklist';
168
import useEngines from '@/lib/engines/use-engines';
17-
import { asyncFilter, asyncForEach, deepEquals } from '@/lib/helpers/javascriptHelpers';
18-
import { getErrorMessage, userError } from '@/lib/user-error';
9+
import { deepEquals } from '@/lib/helpers/javascriptHelpers';
1910
import { useQuery } from '@tanstack/react-query';
2011
import { useCallback } from 'react';
2112

@@ -89,7 +80,6 @@ const mergeDeployment = (
8980

9081
function useDeployment(definitionId: string, initialData?: DeployedProcessInfo) {
9182
const space = useEnvironment();
92-
const { data: session } = useSession();
9383

9484
const { data: engines } = useEngines(space, {
9585
key: [definitionId],
@@ -99,90 +89,6 @@ function useDeployment(definitionId: string, initialData?: DeployedProcessInfo)
9989
},
10090
});
10191

102-
const startInstance = async (versionId: string, variables: { [key: string]: any } = {}) => {
103-
if (!engines?.length) return userError('No fitting engine found');
104-
105-
// TODO: in case of static deployment or different versions on different engines we will have
106-
// to check if the engine can actually be used to start an instance
107-
return await startInstanceOnMachine(definitionId, versionId, engines[0], variables, {
108-
processInitiator: session?.user.id,
109-
spaceIdOfProcessInitiator: space.spaceId,
110-
});
111-
};
112-
113-
const activeStates = ['PAUSED', 'RUNNING', 'READY', 'DEPLOYMENT-WAITING', 'WAITING'];
114-
async function changeInstanceState(
115-
instanceId: string,
116-
stateValidator: (state: InstanceInfo['instanceState']) => boolean,
117-
stateChangeFunction: typeof resumeInstanceOnMachine,
118-
) {
119-
if (!engines) return;
120-
try {
121-
const targetEngines = await asyncFilter(engines, async (engine: Engine) => {
122-
const deployments = await getDeployments([engine]);
123-
124-
return deployments.some((deployment) => {
125-
if (deployment.definitionId !== definitionId) return false;
126-
127-
const instance = deployment.instances.find(
128-
(instance) => instance.processInstanceId === instanceId,
129-
);
130-
if (!instance) return false;
131-
132-
return stateValidator(instance.instanceState);
133-
});
134-
});
135-
136-
await asyncForEach(targetEngines, async (engine) => {
137-
await stateChangeFunction(definitionId, instanceId, engine);
138-
});
139-
} catch (e) {
140-
const message = getErrorMessage(e);
141-
return userError(message);
142-
}
143-
}
144-
145-
async function resumeInstance(instanceId: string) {
146-
// TODO: manage permissions for starting an instance
147-
return await changeInstanceState(
148-
instanceId,
149-
(tokenStates) => tokenStates.some((tokenState) => tokenState === 'PAUSED'),
150-
resumeInstanceOnMachine,
151-
);
152-
}
153-
154-
async function pauseInstance(instanceId: string) {
155-
// TODO: manage permissions for starting an instance
156-
return await changeInstanceState(
157-
instanceId,
158-
(tokenStates) =>
159-
tokenStates.some((state) => activeStates.includes(state) && state !== 'PAUSED'),
160-
pauseInstanceOnMachine,
161-
);
162-
}
163-
164-
async function stopInstance(instanceId: string) {
165-
// TODO: manage permissions for starting an instance
166-
return await changeInstanceState(
167-
instanceId,
168-
(tokenStates) => tokenStates.some((state) => activeStates.includes(state)),
169-
stopInstanceOnMachine,
170-
);
171-
}
172-
173-
async function getStartForm(versionId: string) {
174-
if (!engines?.length) return userError('No fitting engine found');
175-
176-
try {
177-
// TODO: in case of static deployment or different versions on different engines we will have
178-
// to check if the engine can actually be used to start an instance
179-
return await getStartFormFromMachine(definitionId, versionId, engines[0]);
180-
} catch (e) {
181-
const message = getErrorMessage(e);
182-
return userError(message);
183-
}
184-
}
185-
18692
const queryFn = useCallback(async () => {
18793
if (engines?.length) {
18894
// TODO: this only handles situations where we have only a single engine
@@ -212,7 +118,7 @@ function useDeployment(definitionId: string, initialData?: DeployedProcessInfo)
212118
},
213119
});
214120

215-
return { ...query, startInstance, resumeInstance, pauseInstance, stopInstance, getStartForm };
121+
return { ...query };
216122
}
217123

218124
export default useDeployment;
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
'use server';
2+
3+
import db from '@/lib/data/db';
4+
import { DeploymentInput, DeploymentInputSchema } from '../deployment-schema';
5+
import { getCurrentEnvironment } from '@/components/auth';
6+
import { UserErrorType, userError } from '../user-error';
7+
8+
export async function getProcessDeployments(spaceId: string, processId: string) {
9+
const { ability } = await getCurrentEnvironment(spaceId);
10+
11+
if (!ability.can('view', 'Execution'))
12+
return userError('Invalid Permissions', UserErrorType.PermissionError);
13+
14+
const deployments = await db.processDeployment.findMany({
15+
where: { AND: [{ version: { processId } }, { removeTime: null }] },
16+
include: { version: { select: { processId: true } } },
17+
});
18+
19+
return deployments.map((d) => ({ ...d, version: undefined, processId: d.version.processId }));
20+
}
21+
22+
export async function addDeployment(spaceId: string, input: DeploymentInput) {
23+
const { ability } = await getCurrentEnvironment(spaceId);
24+
25+
if (!ability.can('create', 'Execution'))
26+
return userError('Invalid Permissions', UserErrorType.PermissionError);
27+
28+
const data = DeploymentInputSchema.parse(input);
29+
30+
await db.processDeployment.createMany({
31+
data: data.engineIds.map((engineId) => ({ ...data, engineIds: undefined, engineId })),
32+
});
33+
}
34+
35+
export async function updateDeployment(
36+
spaceId: string,
37+
deploymentId: string,
38+
input: Partial<DeploymentInput>,
39+
) {
40+
const { ability } = await getCurrentEnvironment(spaceId);
41+
42+
if (!ability.can('update', 'Execution')) {
43+
return userError('Invalid Permissions', UserErrorType.PermissionError);
44+
}
45+
46+
const data = DeploymentInputSchema.partial().strict().parse(input);
47+
48+
const result = await db.processDeployment.update({
49+
where: { id: deploymentId },
50+
data,
51+
});
52+
53+
return result;
54+
}

0 commit comments

Comments
 (0)