Skip to content

Commit 01081b4

Browse files
committed
feat: add execution log
1 parent 2baa067 commit 01081b4

27 files changed

+3785
-1275
lines changed

demo/.env

Lines changed: 0 additions & 8 deletions
This file was deleted.

demo/App.jsx

Lines changed: 81 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, useRef, useEffect } from 'react';
1+
import React, { useState, useRef, useEffect, useCallback } from 'react';
22

33
import { debounce } from 'min-dash';
44

@@ -27,7 +27,7 @@ function App() {
2727

2828
const [ modeler, setModeler ] = useState(null);
2929

30-
const [ isConnectionConfigured, setIsConnectionConfigured ] = useState(false);
30+
const [ isConnectionConfigured, setIsConnectionConfigured ] = useState(true);
3131

3232
const [ config, setConfig ] = useState(undefined);
3333

@@ -66,18 +66,18 @@ function App() {
6666
const deploy = async () => {
6767
const { xml } = await modeler.saveXML();
6868

69-
const payload = { xml };
69+
const resources = [ { name: 'diagram.bpmn', content: xml } ];
7070

7171
if (form) {
72-
payload.form = form;
72+
resources.push({ name: 'form.form', content: form });
7373
}
7474

7575
const response = await fetch('/api/deploy', {
7676
method: 'POST',
7777
headers: {
7878
'Content-Type': 'application/json'
7979
},
80-
body: JSON.stringify(payload)
80+
body: JSON.stringify({ resources })
8181
});
8282

8383
return await response.json();
@@ -119,17 +119,37 @@ function App() {
119119
.then(response => response.json());
120120
};
121121

122+
const searchJobs = async (processInstanceKey, elementId) => {
123+
return fetch(`/api/searchJobs/${processInstanceKey}?elementId=${elementId}`)
124+
.then(response => response.json());
125+
};
126+
127+
const searchUserTasks = async (processInstanceKey, elementId) => {
128+
return fetch(`/api/searchUserTasks/${processInstanceKey}?elementId=${elementId}`)
129+
.then(response => response.json());
130+
};
131+
132+
const searchMessageSubscriptions = async (processInstanceKey, elementId) => {
133+
return fetch(`/api/searchMessageSubscriptions/${processInstanceKey}?elementId=${elementId}`)
134+
.then(response => response.json());
135+
};
136+
122137
const { current: onConfigChanged } = useRef(debounce(config => setConfig(config), 300));
123138

124139
// eslint-disable-next-line no-undef
125140
const operateURL = process.env.CAMUNDA_OPERATE_BASE_URL;
126141

142+
// eslint-disable-next-line no-undef
143+
const tasklistURL = process.env.CAMUNDA_TASKLIST_BASE_URL;
144+
127145
return (
128146
<>
129147
<div className="modeler" ref={ modelerRef }>
130148
<div id="canvas" className="canvas"></div>
131-
<div id="properties" className="properties-panel"></div>
132-
<div className="task-testing">
149+
<ResizablePanel className="properties-panel" defaultWidth={ 300 } minWidth={ 200 } maxWidth={ 600 }>
150+
<div id="properties" style={ { width: '100%', height: '100%' } }></div>
151+
</ResizablePanel>
152+
<ResizablePanel className="task-testing" defaultWidth={ 500 } minWidth={ 300 } maxWidth={ 800 }>
133153
<TestTab
134154
injector={ injector }
135155
isConnectionConfigured={ isConnectionConfigured }
@@ -160,11 +180,15 @@ function App() {
160180
getProcessInstance,
161181
getProcessInstanceVariables,
162182
getProcessInstanceElementInstances,
163-
getProcessInstanceIncident
183+
getProcessInstanceIncident,
184+
searchJobs,
185+
searchUserTasks,
186+
searchMessageSubscriptions
164187
} }
165188
config={ config }
166189
onConfigChanged={ onConfigChanged }
167190
operateBaseUrl={ operateURL }
191+
tasklistBaseUrl={ tasklistURL }
168192
documentationUrl="https://docs.camunda.io/"
169193
onTaskExecutionStarted={ (element) => {
170194
console.log('Task execution started:', element.id);
@@ -173,7 +197,7 @@ function App() {
173197
console.log('Task execution finished:', element.id, result.success ? 'success' : result.reason, result);
174198
} }
175199
/>
176-
</div>
200+
</ResizablePanel>
177201
</div>
178202
</>
179203
);
@@ -196,4 +220,52 @@ function TestTab(props) {
196220
</TaskTesting>;
197221
}
198222

223+
function ResizablePanel({ children, className, defaultWidth, minWidth, maxWidth }) {
224+
const [ width, setWidth ] = useState(defaultWidth);
225+
const isDragging = useRef(false);
226+
const startX = useRef(0);
227+
const startWidth = useRef(0);
228+
229+
const onMouseDown = useCallback((e) => {
230+
isDragging.current = true;
231+
startX.current = e.clientX;
232+
startWidth.current = width;
233+
document.body.style.cursor = 'col-resize';
234+
document.body.style.userSelect = 'none';
235+
e.preventDefault();
236+
}, [ width ]);
237+
238+
useEffect(() => {
239+
const onMouseMove = (e) => {
240+
if (!isDragging.current) return;
241+
const delta = startX.current - e.clientX;
242+
const newWidth = Math.min(maxWidth, Math.max(minWidth, startWidth.current + delta));
243+
setWidth(newWidth);
244+
};
245+
246+
const onMouseUp = () => {
247+
if (isDragging.current) {
248+
isDragging.current = false;
249+
document.body.style.cursor = '';
250+
document.body.style.userSelect = '';
251+
}
252+
};
253+
254+
document.addEventListener('mousemove', onMouseMove);
255+
document.addEventListener('mouseup', onMouseUp);
256+
257+
return () => {
258+
document.removeEventListener('mousemove', onMouseMove);
259+
document.removeEventListener('mouseup', onMouseUp);
260+
};
261+
}, [ minWidth, maxWidth ]);
262+
263+
return (
264+
<div className={ className } style={ { width: `${width}px` } }>
265+
<div className="resize-handle" onMouseDown={ onMouseDown } />
266+
{ children }
267+
</div>
268+
);
269+
}
270+
199271
export default App;

demo/api.mjs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/**
2+
* Shared Camunda REST API wrapper.
3+
*
4+
* createApi(client) returns an object whose methods mirror the SDK calls used
5+
* by both demo/server.mjs and test/recording/run.mjs. Every method returns a
6+
* `{ success: true, response }` or `{ success: false, error }` object so
7+
* callers never need their own try/catch. Recording stays with the caller.
8+
*
9+
* Response-shape helpers are also exported here since they depend only on the
10+
* result structures produced by these methods.
11+
*/
12+
13+
async function safe(promise) {
14+
try {
15+
const response = await promise;
16+
return { success: true, response };
17+
} catch (err) {
18+
return { success: false, error: err.message };
19+
}
20+
}
21+
22+
/**
23+
* @param {import('@camunda8/sdk').CamundaRestClient} client
24+
*/
25+
export function createApi(client) {
26+
return {
27+
28+
/** @param {Array<{ name: string, content: string }>} resources */
29+
deployResources(resources) {
30+
return safe(client.deployResources(resources));
31+
},
32+
33+
/**
34+
* Starts a process instance scoped to a single element, which is also
35+
* used as the TERMINATE_PROCESS_INSTANCE instruction target.
36+
*/
37+
createProcessInstance({ processDefinitionKey, variables = {}, elementId }) {
38+
return safe(client.createProcessInstance({
39+
processDefinitionKey,
40+
variables,
41+
startInstructions: [ { elementId } ],
42+
runtimeInstructions: [
43+
{ type: 'TERMINATE_PROCESS_INSTANCE', afterElementId: elementId }
44+
]
45+
}));
46+
},
47+
48+
searchProcessInstances(processInstanceKey) {
49+
return safe(client.searchProcessInstances({
50+
filter: { processInstanceKey }
51+
}));
52+
},
53+
54+
searchVariables(processInstanceKey) {
55+
return safe(client.searchVariables({
56+
filter: { processInstanceKey }
57+
}));
58+
},
59+
60+
searchJobs(processInstanceKey, elementId) {
61+
return safe(client.callApiEndpoint({
62+
urlPath: 'jobs/search',
63+
method: 'POST',
64+
body: {
65+
filter: {
66+
processInstanceKey,
67+
...(elementId && { elementId })
68+
}
69+
}
70+
}));
71+
},
72+
73+
searchUserTasks(processInstanceKey, elementId) {
74+
return safe(client.searchUserTasks({
75+
filter: {
76+
processInstanceKey,
77+
...(elementId && { elementId })
78+
}
79+
}));
80+
},
81+
82+
searchMessageSubscriptions(processInstanceKey, elementId) {
83+
return safe(client.callApiEndpoint({
84+
urlPath: 'message-subscriptions/search',
85+
method: 'POST',
86+
body: {
87+
filter: {
88+
processInstanceKey,
89+
...(elementId && { elementId })
90+
}
91+
}
92+
}));
93+
},
94+
95+
searchElementInstances(processInstanceKey) {
96+
return safe(client.searchElementInstances({
97+
filter: { processInstanceKey }
98+
}));
99+
},
100+
101+
searchIncidents(processInstanceKey) {
102+
return safe(client.searchIncidents({
103+
filter: { processInstanceKey }
104+
}));
105+
}
106+
};
107+
}
108+
109+
export function getProcessDefinitionKey(deployResponse, processId) {
110+
const { deployments = [] } = deployResponse;
111+
for (const deployment of deployments) {
112+
if ('processDefinition' in deployment) {
113+
const { processDefinition } = deployment;
114+
if (processDefinition.processDefinitionId === processId) {
115+
return processDefinition.processDefinitionKey;
116+
}
117+
}
118+
}
119+
return null;
120+
}
121+
122+
export function getProcessInstanceKey(response) {
123+
const { processInstanceKey } = response;
124+
return processInstanceKey || null;
125+
}
126+
127+
function getProcessInstance(response, processInstanceKey) {
128+
const { items = [] } = response;
129+
if (!items.length) return null;
130+
return items.find(item => item.processInstanceKey === processInstanceKey) || null;
131+
}
132+
133+
export function getProcessInstanceState(response, processInstanceKey) {
134+
const processInstance = getProcessInstance(response, processInstanceKey);
135+
return processInstance ? processInstance.state : null;
136+
}
137+
138+
export function hasProcessInstanceIncident(response, processInstanceKey) {
139+
const processInstance = getProcessInstance(response, processInstanceKey);
140+
return processInstance ? (processInstance.hasIncident || false) : false;
141+
}

demo/fixtures/config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ const input = {
1515
}, null, 2),
1616
'ServiceTask_5': JSON.stringify({
1717
jobWorkerDelay: 5,
18-
failJob: false
18+
failJob: false,
19+
terminate: false
1920
}, null, 2)
2021
};
2122

0 commit comments

Comments
 (0)