Skip to content

Commit 7ec7ef2

Browse files
committed
WIP status data
1 parent 04431bc commit 7ec7ef2

File tree

6 files changed

+151
-18
lines changed

6 files changed

+151
-18
lines changed

lib/ExecutionLog.js

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,21 +190,50 @@ export default class ExecutionLog {
190190
*
191191
* @param {TaskExecutionStatus} status
192192
* @param {number} [timestamp]
193+
* @param {Object} [data]
193194
*
194195
* @returns {ExecutionLogEntry[]}
195196
*/
196-
recordStatus(status, timestamp = Date.now()) {
197+
recordStatus(status, timestamp = Date.now(), data = {}) {
197198
this._statusEntries = [ ...this._statusEntries, {
198199
type: ENTRY_TYPE.STATUS,
199200
status,
200-
timestamp
201+
timestamp,
202+
data
201203
} ];
202204

203205
this._updateEntries();
204206

205207
return this._entries;
206208
}
207209

210+
/**
211+
* Retroactively attach data to the most recent status entry matching the
212+
* given status. This is useful when response data only becomes available
213+
* after a status has already been recorded (e.g. the deploy response is
214+
* available only after the deploy call succeeds, at which point the status
215+
* has already moved past 'deploying').
216+
*
217+
* @param {TaskExecutionStatus} status
218+
* @param {Object} data
219+
*
220+
* @returns {ExecutionLogEntry[]}
221+
*/
222+
updateStatusData(status, data) {
223+
const idx = this._statusEntries.findLastIndex(e => e.status === status);
224+
225+
if (idx === -1) {
226+
return this._entries;
227+
}
228+
229+
this._statusEntries = [ ...this._statusEntries ];
230+
this._statusEntries[idx] = { ...this._statusEntries[idx], data };
231+
232+
this._updateEntries();
233+
234+
return this._entries;
235+
}
236+
208237
/**
209238
* Record a poll entry and refresh poll snapshots.
210239
*

lib/TaskExecution.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ export default class TaskExecution extends EventEmitter {
123123
return;
124124
}
125125

126+
this._changeStatus('deploying', { response: deploymentResult.response });
127+
126128
this._changeStatus('starting-instance');
127129

128130
const startInstanceResult = await this._api.startInstance(processDefinitionKey, element.id, variables);
@@ -147,7 +149,9 @@ export default class TaskExecution extends EventEmitter {
147149
return;
148150
}
149151

150-
this._changeStatus('executing', processInstanceKey);
152+
this._changeStatus('starting-instance', { processInstanceKey, response: startInstanceResult.response });
153+
154+
this._changeStatus('executing', { processInstanceKey });
151155

152156
const intervalCallback = async () => {
153157
const processInstanceResult = await this._api.getProcessInstance(processInstanceKey);
@@ -325,14 +329,14 @@ export default class TaskExecution extends EventEmitter {
325329
}
326330

327331
/** @param {TaskExecutionStatus} status */
328-
_changeStatus(status, ...args) {
332+
_changeStatus(status, data) {
329333

330-
if (this._status === status) {
334+
if (this._status === status && !data) {
331335
return;
332336
}
333337

334338
this._status = status;
335-
this.emit('taskExecution.status.changed', status, ...args);
339+
this.emit('taskExecution.status.changed', status, data);
336340
}
337341
}
338342

lib/components/Output/ExecutionLog.jsx

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,52 @@ function getExecutionListenerLabel(jobData) {
8888
return 'execution listener';
8989
}
9090

91+
function getStatusDetails(entry) {
92+
const data = entry.data;
93+
94+
if (!data) {
95+
return [];
96+
}
97+
98+
const details = [];
99+
100+
if (entry.status === 'deploying') {
101+
const response = data.response;
102+
if (!response) return [];
103+
const deployment = response.deployments?.[0];
104+
const processDef = deployment?.processDefinition;
105+
106+
if (processDef?.processDefinitionId) details.push({ label: 'Process', value: processDef.processDefinitionId });
107+
if (processDef?.processDefinitionKey) details.push({ label: 'Definition Key', value: processDef.processDefinitionKey });
108+
if (processDef?.processDefinitionVersion) details.push({ label: 'Version', value: String(processDef.processDefinitionVersion) });
109+
if (response.deploymentKey) details.push({ label: 'Deployment Key', value: response.deploymentKey });
110+
}
111+
112+
if (entry.status === 'starting-instance') {
113+
const response = data.response;
114+
if (data.processInstanceKey) details.push({ label: 'Instance Key', value: data.processInstanceKey });
115+
if (response?.processDefinitionId) details.push({ label: 'Process', value: response.processDefinitionId });
116+
if (response?.processDefinitionKey) details.push({ label: 'Definition Key', value: response.processDefinitionKey });
117+
}
118+
119+
if (entry.status === 'completed' || entry.status === 'terminated') {
120+
if (data.processInstanceKey) details.push({ label: 'Instance Key', value: data.processInstanceKey });
121+
}
122+
123+
if (entry.status === 'incident') {
124+
if (data.processInstanceKey) details.push({ label: 'Instance Key', value: data.processInstanceKey });
125+
if (data.incident?.errorType) details.push({ label: 'Error Type', value: data.incident.errorType });
126+
if (data.incident?.errorMessage) details.push({ label: 'Error', value: data.incident.errorMessage });
127+
}
128+
129+
return details;
130+
}
131+
91132
function getEntryDetails(entry) {
133+
if (entry.type === 'status') {
134+
return getStatusDetails(entry);
135+
}
136+
92137
if (entry.type === 'job') {
93138
const data = entry.data;
94139
const details = [];
@@ -184,12 +229,23 @@ export function ExecutionLog({ entries, tasklistBaseUrl, currentOperateUrl }) {
184229
}
185230

186231
function StatusEntry({ entry }) {
232+
const [ expanded, setExpanded ] = useState(false);
187233
const label = STATUS_LABELS[entry.status];
188234

189235
if (!label) {
190236
return null;
191237
}
192238

239+
const details = getStatusDetails(entry);
240+
const hasDetails = details.length > 0;
241+
const isExpandable = hasDetails;
242+
243+
const handleExpandToggle = () => {
244+
if (isExpandable) {
245+
setExpanded(!expanded);
246+
}
247+
};
248+
193249
const wrapperClass = [
194250
'execution-log__entry-wrapper',
195251
entry.status === 'completed' ? 'execution-log__entry-wrapper--success' : '',
@@ -198,12 +254,43 @@ function StatusEntry({ entry }) {
198254
entry.status === 'canceled' ? 'execution-log__entry-wrapper--canceled' : ''
199255
].filter(Boolean).join(' ');
200256

257+
const entryClass = [
258+
'execution-log__entry',
259+
isExpandable ? 'execution-log__entry--expandable' : ''
260+
].filter(Boolean).join(' ');
261+
201262
return (
202263
<li className={ wrapperClass }>
203-
<div className="execution-log__entry" data-type="status" data-timestamp={ entry.timestamp }>
264+
<div
265+
className={ entryClass }
266+
data-type="status"
267+
data-timestamp={ entry.timestamp }
268+
onClick={ isExpandable ? handleExpandToggle : undefined }
269+
>
204270
<span className="execution-log__timestamp">{ formatTime(entry.timestamp) }</span>
205271
<span className="execution-log__label">{ label }</span>
272+
{ isExpandable && (
273+
<span
274+
className="execution-log__toggle"
275+
onClick={ (event) => {
276+
event.stopPropagation();
277+
handleExpandToggle();
278+
} }
279+
>
280+
{ expanded ? <ChevronDown size={ 14 } /> : <ChevronRight size={ 14 } /> }
281+
</span>
282+
) }
206283
</div>
284+
{ expanded && hasDetails && (
285+
<dl className="execution-log__details">
286+
{ details.map(({ label, value }, i) => (
287+
<div key={ i } className="execution-log__details-row">
288+
<dt className="execution-log__details-label">{ label }</dt>
289+
<dd className="execution-log__details-value">{ value }</dd>
290+
</div>
291+
)) }
292+
</dl>
293+
) }
207294
</li>
208295
);
209296
}

lib/components/TaskTesting/TaskTesting.js

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -279,22 +279,32 @@ export default function TaskTesting({
279279

280280
/**
281281
* @param {import('../../types').TaskExecutionStatus} status
282-
* @param {string} [processInstanceKey]
282+
* @param {Object} [data]
283+
* @param {string} [data.processInstanceKey]
284+
* @param {Object} [data.response]
283285
*/
284-
const handleStatusChange = (status, processInstanceKey) => {
286+
const handleStatusChange = (status, data) => {
285287
setTaskExecutionStatus(status);
286288

287-
if (processInstanceKey && operateBaseUrl) {
288-
const url = getOperateUrl(operateBaseUrl, processInstanceKey);
289+
if (data?.processInstanceKey && operateBaseUrl) {
290+
const url = getOperateUrl(operateBaseUrl, data.processInstanceKey);
289291
setCurrentOperateUrl(url);
290292
}
291293

292294
if (status === 'idle') {
293295
setCurrentOperateUrl(undefined);
294296
}
295297

298+
// A second event for the same status carries response data;
299+
// update the existing log entry instead of adding a new one.
300+
if (data?.response) {
301+
const updated = executionLogRef.current.updateStatusData(status, data);
302+
setExecutionLog(updated);
303+
return;
304+
}
305+
296306
// Feed status changes to the execution log model
297-
const updated = executionLogRef.current.recordStatus(status, Date.now());
307+
const updated = executionLogRef.current.recordStatus(status, Date.now(), data);
298308
setExecutionLog(updated);
299309
};
300310

@@ -303,13 +313,13 @@ export default function TaskTesting({
303313

304314
// Feed terminal status to the execution log model
305315
if (result.success) {
306-
executionLogRef.current.recordStatus('completed', Date.now());
316+
executionLogRef.current.recordStatus('completed', Date.now(), result);
307317
} else if (isIncidentResult(result)) {
308-
executionLogRef.current.recordStatus('incident', Date.now());
318+
executionLogRef.current.recordStatus('incident', Date.now(), result);
309319
} else if (isTerminatedResult(result)) {
310-
executionLogRef.current.recordStatus('terminated', Date.now());
320+
executionLogRef.current.recordStatus('terminated', Date.now(), result);
311321
} else if (result.reason === TASK_EXECUTION_REASON.USER_CANCEL) {
312-
executionLogRef.current.recordStatus('canceled', Date.now());
322+
executionLogRef.current.recordStatus('canceled', Date.now(), result);
313323
}
314324

315325
setExecutionLog(executionLogRef.current.getEntries());

lib/types.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ export type StatusEntry = {
157157
type: 'status';
158158
status: TaskExecutionStatus;
159159
timestamp: number;
160+
data?: any;
160161
};
161162

162163
export type JobEntry = {

test/TaskExecution.spec.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,10 @@ describe('TaskExecution', function() {
7070

7171
// then
7272
expect(statusChangeSpy).to.have.been.calledWith('deploying');
73+
expect(statusChangeSpy).to.have.been.calledWith('deploying', { response: DEFAULT_DEPLOY_RESPONSE });
7374
expect(statusChangeSpy).to.have.been.calledWith('starting-instance');
74-
expect(statusChangeSpy).to.have.been.calledWith('executing');
75+
expect(statusChangeSpy).to.have.been.calledWith('starting-instance', { processInstanceKey: '2251799813755922', response: DEFAULT_START_INSTANCE_RESPONSE });
76+
expect(statusChangeSpy).to.have.been.calledWith('executing', { processInstanceKey: '2251799813755922' });
7577

7678
expect(api.deploy).to.have.been.calledOnce;
7779
expect(api.startInstance).to.have.been.calledOnce;
@@ -541,7 +543,7 @@ describe('TaskExecution', function() {
541543
await clock.tickAsync(500);
542544

543545
// assume
544-
expect(statusChangeSpy).to.have.been.calledWith('executing');
546+
expect(statusChangeSpy).to.have.been.calledWith('executing', { processInstanceKey: '2251799813755922' });
545547
expect(api.startInstance).to.have.been.calledOnce;
546548

547549
// when

0 commit comments

Comments
 (0)