Skip to content

Commit 7a63537

Browse files
authored
Merge branch 'main' into ms/user-task-data-access
2 parents ca6c51d + 25fd372 commit 7a63537

6 files changed

Lines changed: 216 additions & 7 deletions

File tree

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,16 @@ class Engine {
129129
* @param {string} the version of the process to deploy
130130
*/
131131
async deployProcessVersion(definitionId, versionId) {
132+
const otherVersions = this.versions.filter((version) => version !== versionId);
133+
otherVersions.forEach((version) => {
134+
const process = this._versionProcessMapping[version];
135+
136+
// deactivate other versions so they don't keep spawning new instances automatically
137+
if (process) {
138+
process.undeploy();
139+
}
140+
});
141+
132142
if (!this._versionProcessMapping[versionId]) {
133143
// Fetch the stored BPMN
134144
const bpmn = await distribution.db.getProcessVersion(definitionId, versionId);
@@ -213,6 +223,22 @@ class Engine {
213223
this._versionProcessMapping[versionId] = process;
214224
this._versionBpmnMapping[versionId] = bpmn;
215225
this.versions.push(versionId);
226+
} else if (!this._versionProcessMapping[versionId].isDeployed()) {
227+
// activate the process so auto-start events like timer events are allowed to trigger new
228+
// instances
229+
this._versionProcessMapping[versionId].deploy();
230+
}
231+
}
232+
233+
/**
234+
* Removes the deployed state from the process version in the NeoBPMN Engine preventing it from starting instances
235+
*
236+
* @param {string} the version of the process to undeploy
237+
*/
238+
undeployProcessVersion(versionId) {
239+
const process = this._versionProcessMapping[versionId];
240+
if (process && process.isDeployed()) {
241+
process.undeploy();
216242
}
217243
}
218244

@@ -1014,6 +1040,9 @@ class Engine {
10141040
* Clean up some data when the engine is supposed to be removed
10151041
*/
10161042
destroy() {
1043+
for (const version of this.versions) {
1044+
this._versionProcessMapping[version].undeploy();
1045+
}
10171046
for (const instanceId of this.instanceIDs) {
10181047
this.deleteInstance(instanceId);
10191048
}

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,7 @@ const Management = {
5555
const engine = this.ensureProcessEngine(definitionId);
5656

5757
// ensure that the version is deployed
58-
if (!engine.versions.includes(version)) {
59-
await engine.deployProcessVersion(definitionId, version);
60-
}
58+
await engine.deployProcessVersion(definitionId, version);
6159

6260
return engine;
6361
},

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

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,4 +393,71 @@ module.exports = (path, management) => {
393393
});
394394
},
395395
);
396+
397+
network.put(`${path}/:definitionId/active`, { cors: true }, async (req) => {
398+
const { definitionId } = req.params;
399+
400+
const {
401+
body: { active },
402+
} = req;
403+
404+
if (active === true) {
405+
return {
406+
statusCode: 400,
407+
mimeType: 'text/plain',
408+
response:
409+
'Cannot set active true on a process. Please select a specific version to activate.',
410+
};
411+
} else if (active === false) {
412+
const engine = await management.getEngineWithDefinitionId(definitionId);
413+
414+
if (engine) {
415+
engine.versions.forEach((version) => engine.undeployProcessVersion(version));
416+
}
417+
418+
return {
419+
statusCode: 200,
420+
mimeType: 'application/json',
421+
response: '{}',
422+
};
423+
} else {
424+
return {
425+
statusCode: 400,
426+
mimeType: 'text/plain',
427+
response:
428+
'This endpoint expects the request body to contain an entry called active with a boolean value of "false".',
429+
};
430+
}
431+
});
432+
433+
network.put(`${path}/:definitionId/versions/:version/active`, { cors: true }, async (req) => {
434+
const { definitionId, version } = req.params;
435+
436+
const {
437+
body: { active },
438+
} = req;
439+
440+
if (active === true) {
441+
await management.ensureProcessEngineWithVersion(definitionId, version);
442+
} else if (active === false) {
443+
const engine = await management.getEngineWithDefinitionId(definitionId);
444+
445+
if (engine) {
446+
engine.undeployProcessVersion(version);
447+
}
448+
} else {
449+
return {
450+
statusCode: 400,
451+
mimeType: 'text/plain',
452+
response:
453+
'This endpoint expects the request body to contain an entry called active with a boolean value',
454+
};
455+
}
456+
457+
return {
458+
statusCode: 200,
459+
mimeType: 'application/json',
460+
response: '{}',
461+
};
462+
});
396463
};

src/management-system-v2/lib/engines/deployment.ts

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,17 @@ async function deployProcessToMachines(
106106
});
107107

108108
await Promise.all(allMachineRequests);
109+
110+
// TODO: if we handle static deployment with machine mapping of process elements in the future
111+
// we might need to make sure that we only activate the process in a way that start events that are not mapped to a machine are not triggered on that machine
112+
// (maybe we need to split the process into multiple sections and deploy them seperately
113+
await asyncForEach(machines, async (machine) => {
114+
await asyncForEach(processesExportData, async (process) => {
115+
await asyncForEach(Object.keys(process.versions), async (version) => {
116+
await changeDeploymentActivation(machine, process.definitionId, version, true);
117+
});
118+
});
119+
});
109120
} catch (error) {
110121
// TODO: don't remove the whole process when deploying a single version fails
111122
await asyncForEach(Object.values(processesExportData), async ({ definitionId }) => {
@@ -153,6 +164,8 @@ async function dynamicDeployment(
153164
}
154165

155166
await deployProcessToMachines([preferredMachine], processesExportData);
167+
168+
return [preferredMachine];
156169
}
157170

158171
async function staticDeployment(
@@ -198,6 +211,8 @@ async function staticDeployment(
198211
// targetedMachines.push(forceMachine);
199212

200213
await deployProcessToMachines(targetedMachines, processesExportData);
214+
215+
return targetedMachines;
201216
}
202217

203218
export async function deployProcess(
@@ -230,9 +245,9 @@ export async function deployProcess(
230245
);
231246

232247
if (method === 'static') {
233-
await staticDeployment(definitionId, version, processesExportData, machines);
248+
return await staticDeployment(definitionId, version, processesExportData, machines);
234249
} else {
235-
await dynamicDeployment(definitionId, version, processesExportData, machines);
250+
return await dynamicDeployment(definitionId, version, processesExportData, machines);
236251
}
237252
}
238253
export type ImportInformation = { definitionId: string; processId: string; version: number };
@@ -341,6 +356,31 @@ export async function getDeployment(engine: Engine, definitionId: string) {
341356
return deployment as DeployedProcessInfo;
342357
}
343358

359+
export async function changeDeploymentActivation(
360+
engine: Engine,
361+
definitionId: string,
362+
version: string | undefined,
363+
value: boolean,
364+
) {
365+
if (version) {
366+
await engineRequest({
367+
method: 'put',
368+
endpoint: '/process/:definitionId/versions/:version/active',
369+
engine,
370+
pathParams: { definitionId, version },
371+
body: { active: value },
372+
});
373+
} else {
374+
await engineRequest({
375+
method: 'put',
376+
endpoint: '/process/:definitionId/active',
377+
engine,
378+
pathParams: { definitionId },
379+
body: { active: value },
380+
});
381+
}
382+
}
383+
344384
export async function getProcessImageFromMachine(
345385
engine: Engine,
346386
definitionId: string,

src/management-system-v2/lib/engines/endpoints/endpoints.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@
8888
"/process/:definitionId/instance/:instanceID/instanceState": {},
8989
"/process/:definitionId/instance/:instanceId/tokens/:tokenId": {},
9090
"/process/:definitionId/instance/:instanceId/tokens/:tokenId/currentFlowNodeState": {},
91+
"/process/:definitionId/active": {},
92+
"/process/:definitionId/versions/:version/active": {},
9193
"/process/:definitionId/versions/:version/start-form": {},
9294
"/process/:definitionId/user-tasks/:fileName": {},
9395
"/process/:definitionId/script-tasks/:fileName": {},

src/management-system-v2/lib/engines/server-actions.ts

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ import {
77
getDeployment as fetchDeployment,
88
getProcessImageFromMachine,
99
removeDeploymentFromMachines,
10+
changeDeploymentActivation as _changeDeploymentActivation,
11+
DeployedProcessInfo,
1012
} from './deployment';
1113
import { Engine, SpaceEngine } from './machines';
1214
import { savedEnginesToEngines } from './saved-engines-helpers';
13-
import { getCurrentEnvironment, getCurrentUser } from '@/components/auth';
15+
import { getCurrentEnvironment } from '@/components/auth';
1416
import { enableUseDB } from 'FeatureFlags';
1517
import { getDbEngines, getDbEngineByAddress } from '@/lib/data/db/engines';
1618
import { asyncFilter, asyncMap, asyncForEach } from '../helpers/javascriptHelpers';
@@ -107,7 +109,51 @@ export async function deployProcess(
107109

108110
if (engines.length === 0) throw new UserFacingError('No fitting engine found.');
109111

110-
await _deployProcess(definitionId, versionId, spaceId, method, engines);
112+
const processAlreadyDeployedInfo = await asyncMap(engines, async (engine) => {
113+
let deployment;
114+
try {
115+
deployment = await fetchDeployment(engine, definitionId);
116+
// ignore not found errors on engines that don't have a deployment of the process
117+
} catch (err) {
118+
deployment = undefined;
119+
}
120+
return [engine, deployment] as const;
121+
});
122+
123+
function withDeployment(
124+
info: (typeof processAlreadyDeployedInfo)[number],
125+
): info is readonly [Engine, DeployedProcessInfo] {
126+
return !!info[1];
127+
}
128+
const enginesWithDeployment = processAlreadyDeployedInfo.filter(withDeployment);
129+
130+
// check if the version is already deployed to some engine since we don't
131+
// need to redeploy it in that case
132+
if (
133+
!_forceEngine &&
134+
enginesWithDeployment.some(([_, deployment]) =>
135+
deployment.versions.some((version) => version.versionId === versionId),
136+
)
137+
) {
138+
return;
139+
}
140+
141+
if (!_forceEngine && enginesWithDeployment.length) {
142+
// check if an engine already has another version in which case that engine is selected
143+
engines = enginesWithDeployment.map(([engine]) => engine);
144+
}
145+
146+
const deployedTo = await _deployProcess(definitionId, versionId, spaceId, method, engines);
147+
148+
// deactivate the process on all engines that have a deployment but which were not target of the
149+
// new deployment
150+
await Promise.allSettled(
151+
enginesWithDeployment.map(async ([engine]) => {
152+
if (!deployedTo.some((dE) => dE.id === engine.id)) {
153+
await _changeDeploymentActivation(engine, definitionId, undefined, false);
154+
}
155+
}),
156+
);
111157
} catch (e) {
112158
const message = getErrorMessage(e);
113159
return userError(message);
@@ -131,6 +177,33 @@ export async function removeDeployment(definitionId: string, spaceId: string) {
131177
}
132178
}
133179

180+
export async function changeDeploymentActivation(
181+
definitionId: string,
182+
spaceId: string,
183+
version: string,
184+
value: boolean,
185+
) {
186+
try {
187+
const engines = await getCorrectTargetEngines(spaceId, false, async (engine: Engine) => {
188+
const deployments = await fetchDeployments([engine]);
189+
190+
return deployments.some(
191+
(deployment) =>
192+
deployment.definitionId === definitionId &&
193+
deployment.versions.some((v) => v.versionId === version),
194+
);
195+
});
196+
197+
if (!engines.length)
198+
throw new Error('There is no available engine with the requested process version.');
199+
200+
await _changeDeploymentActivation(engines[0], definitionId, version, value);
201+
} catch (e) {
202+
const message = getErrorMessage(e);
203+
return userError(message);
204+
}
205+
}
206+
134207
export async function getAvailableTaskListEntries(spaceId: string, engines: Engine[]) {
135208
try {
136209
if (!enableUseDB)

0 commit comments

Comments
 (0)