Skip to content

Commit 385e71e

Browse files
committed
WIP big refactor
1 parent 2116ef3 commit 385e71e

File tree

11 files changed

+359
-413
lines changed

11 files changed

+359
-413
lines changed

lib/ExecutionLog.js

Lines changed: 68 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -1,114 +1,48 @@
11
/**
22
* @typedef {import('./types').ExecutionLogEntry} ExecutionLogEntry
3-
* @typedef {import('./types').TaskExecutionStatus} TaskExecutionStatus
3+
* @typedef {import('./types').ExecutionLogEntryStatus} TaskExecutionStatus
4+
* @typedef {import('./types').ExecutionLogElementInstanceEntry} ElementInstanceEntry
45
* @typedef {import('./types').TaskExecutionPollData} TaskExecutionPollData
5-
* @typedef {import('./types').ElementInstanceEntry} ElementInstanceEntry
6+
* @typedef {import('./types').TaskExecutionState} TaskExecutionState
7+
*
8+
* @typedef {import('@camunda8/sdk/dist/c8/lib/C8Dto').DeployResourceResponse} DeployResourceResponse
9+
* @typedef {import('./types').ApiResponse<DeployResourceResponse>} DeployResponse
10+
*
11+
* @typedef {import('@camunda8/sdk/dist/c8/lib/C8Dto').CreateProcessInstanceResponse} CreateProcessInstanceResponse
12+
* @typedef {import('./types').ApiResponse<CreateProcessInstanceResponse>} StartInstanceResponse
613
*/
714

8-
const ENTRY_TYPE = /** @type {const} */ ({
15+
export const EXECUTION_LOG_ENTRY_TYPE = {
916
STATUS: 'status',
1017
JOB: 'job',
1118
USER_TASK: 'user-task',
1219
MESSAGE_SUBSCRIPTION: 'message-subscription',
1320
ELEMENT_INSTANCE: 'element-instance'
14-
});
15-
16-
const TASK_STATUS = /** @type {const} */ ({
17-
EXECUTING: 'executing',
18-
COMPLETED: 'completed',
19-
INCIDENT: 'incident',
20-
TERMINATED: 'terminated',
21-
CANCELED: 'canceled'
22-
});
21+
};
2322

2423
const TIMESTAMP_FIELDS = {
25-
[ENTRY_TYPE.JOB]: [ 'endTime' ],
26-
[ENTRY_TYPE.USER_TASK]: [ 'completionDate', 'creationDate' ],
27-
[ENTRY_TYPE.ELEMENT_INSTANCE]: [ 'endDate', 'startDate' ]
24+
[EXECUTION_LOG_ENTRY_TYPE.JOB]: [ 'endTime' ],
25+
[EXECUTION_LOG_ENTRY_TYPE.USER_TASK]: [ 'completionDate', 'creationDate' ],
26+
[EXECUTION_LOG_ENTRY_TYPE.ELEMENT_INSTANCE]: [ 'endDate', 'startDate' ]
2827
};
2928

30-
const SIMPLE_POLL_SLICES = [
29+
const POLL_RESPONSE_FIELDS = [
3130
{
3231
resultField: 'jobsResult',
33-
entryType: ENTRY_TYPE.JOB
32+
entryType: EXECUTION_LOG_ENTRY_TYPE.JOB
3433
},
3534
{
3635
resultField: 'userTasksResult',
37-
entryType: ENTRY_TYPE.USER_TASK
36+
entryType: EXECUTION_LOG_ENTRY_TYPE.USER_TASK
3837
},
3938
{
4039
resultField: 'messageSubscriptionsResult',
41-
entryType: ENTRY_TYPE.MESSAGE_SUBSCRIPTION
40+
entryType: EXECUTION_LOG_ENTRY_TYPE.MESSAGE_SUBSCRIPTION
4241
}
4342
];
4443

45-
const SNAPSHOT_ENTRY_TYPES = [
46-
ENTRY_TYPE.JOB,
47-
ENTRY_TYPE.USER_TASK,
48-
ENTRY_TYPE.MESSAGE_SUBSCRIPTION,
49-
ENTRY_TYPE.ELEMENT_INSTANCE
50-
];
51-
52-
function createEmptyPollEntriesByType() {
53-
return {
54-
[ENTRY_TYPE.JOB]: [],
55-
[ENTRY_TYPE.USER_TASK]: [],
56-
[ENTRY_TYPE.MESSAGE_SUBSCRIPTION]: [],
57-
[ENTRY_TYPE.ELEMENT_INSTANCE]: []
58-
};
59-
}
60-
6144
/**
62-
* Check whether a set of log entries represents a finished execution.
63-
*
64-
* @param {ExecutionLogEntry[]} entries
65-
*
66-
* @returns {boolean}
67-
*/
68-
export function isFinished(entries) {
69-
return entries.some(
70-
e => e.type === ENTRY_TYPE.STATUS && (
71-
e.status === TASK_STATUS.COMPLETED
72-
|| e.status === TASK_STATUS.INCIDENT
73-
|| e.status === TASK_STATUS.TERMINATED
74-
|| e.status === TASK_STATUS.CANCELED
75-
)
76-
);
77-
}
78-
79-
/**
80-
* Derive the display-ready list of entries from raw entries.
81-
* - When finished, the transient `executing` status is dropped entirely.
82-
* - While running, `executing` is moved to the end so it always appears last.
83-
*
84-
* @param {ExecutionLogEntry[]} entries
85-
*
86-
* @returns {ExecutionLogEntry[]}
87-
*/
88-
export function getDisplayEntries(entries) {
89-
const finished = isFinished(entries);
90-
const withoutExecuting = [];
91-
const executing = [];
92-
93-
for (const entry of entries) {
94-
if (entry.type === ENTRY_TYPE.STATUS && entry.status === TASK_STATUS.EXECUTING) {
95-
executing.push(entry);
96-
} else {
97-
withoutExecuting.push(entry);
98-
}
99-
}
100-
101-
withoutExecuting.sort((a, b) => a.timestamp - b.timestamp);
102-
103-
if (finished) {
104-
return withoutExecuting;
105-
}
106-
107-
return [ ...withoutExecuting, ...executing ];
108-
}
109-
110-
/**
111-
* Get the most meaningful timestamp from an API result item.
45+
* Get the most meaningful timestamp from
11246
*
11347
* job → endTime (when the job last changed state)
11448
* user-task → completionDate, then creationDate
@@ -117,11 +51,10 @@ export function getDisplayEntries(entries) {
11751
*
11852
* @param {string} type
11953
* @param {any} data
120-
* @param {number} pollTimestamp
12154
*
12255
* @returns {number}
12356
*/
124-
function getTimestamp(type, data, pollTimestamp) {
57+
function getTimestamp(type, data) {
12558
const candidateFields = TIMESTAMP_FIELDS[type] || [];
12659

12760
for (const field of candidateFields) {
@@ -137,7 +70,8 @@ function getTimestamp(type, data, pollTimestamp) {
13770
}
13871
}
13972

140-
return pollTimestamp;
73+
// TODO: for message-subscription, we currently have no reliable timestamp field. We could consider using the poll timestamp, but that would require passing it through multiple layers. For now, we just use the current time as a fallback.
74+
return Date.now();
14175
}
14276

14377
/**
@@ -171,12 +105,13 @@ export default class ExecutionLog {
171105
* @param {Object} [injector]
172106
*/
173107
constructor(injector) {
108+
this._state = null;
174109

175-
/** @type {ExecutionLogEntry[]} */
176-
this._statusEntries = [];
110+
this._deployResponse = null;
111+
112+
this._startInstanceResponse = null;
177113

178-
/** @type {Record<string, ExecutionLogEntry[]>} */
179-
this._pollEntriesByType = createEmptyPollEntriesByType();
114+
this._pollResponse = null;
180115

181116
/** @type {ExecutionLogEntry[]} */
182117
this._entries = [];
@@ -186,135 +121,103 @@ export default class ExecutionLog {
186121
}
187122

188123
/**
189-
* Record a status entry.
124+
* Add state.
190125
*
191-
* @param {TaskExecutionStatus} status
126+
* @param {TaskExecutionState} state
192127
* @param {number} [timestamp]
193-
* @param {Object} [data]
194128
*
195129
* @returns {ExecutionLogEntry[]}
196130
*/
197-
recordStatus(status, timestamp = Date.now(), data = {}) {
198-
this._statusEntries = [ ...this._statusEntries, {
199-
type: ENTRY_TYPE.STATUS,
200-
status,
201-
timestamp,
202-
data
203-
} ];
131+
addState(state, timestamp = Date.now()) {
132+
this._state = { type: state, timestamp };
204133

205134
this._updateEntries();
206135

207136
return this._entries;
208137
}
209138

210139
/**
211-
* Record a poll entry and refresh poll snapshots.
140+
* Set deploy response.
212141
*
213-
* @param {TaskExecutionPollData} pollEntry
142+
* @param {DeployResponse} deployResponse
214143
*
215144
* @returns {ExecutionLogEntry[]}
216145
*/
217-
recordPoll(pollEntry) {
218-
const {
219-
jobsResult,
220-
userTasksResult,
221-
messageSubscriptionsResult,
222-
elementInstancesResult,
223-
elementId,
224-
timestamp
225-
} = /** @type {any} */ (pollEntry);
226-
227-
const pollData = {
228-
jobsResult,
229-
userTasksResult,
230-
messageSubscriptionsResult
231-
};
232-
233-
const nextPollEntriesByType = { ...this._pollEntriesByType };
234-
235-
// Simple slices (jobs, user tasks, message subscriptions)
236-
for (const { resultField, entryType } of SIMPLE_POLL_SLICES) {
237-
const result = pollData[resultField];
238-
239-
if (result.success) {
240-
nextPollEntriesByType[entryType] = this._buildSimpleSliceEntries(
241-
result.response.items,
242-
entryType,
243-
timestamp
244-
);
245-
}
246-
}
247-
248-
// Element instances
249-
if (elementInstancesResult.success) {
250-
nextPollEntriesByType[ENTRY_TYPE.ELEMENT_INSTANCE] = this._buildElementInstanceEntries({
251-
allInstances: elementInstancesResult.response.items || [],
252-
elementId,
253-
timestamp
254-
});
255-
}
256-
257-
this._pollEntriesByType = nextPollEntriesByType;
146+
setDeployResponse(deployResponse) {
147+
this._deployResponse = deployResponse;
258148

259149
this._updateEntries();
260150

261151
return this._entries;
262152
}
263153

264154
/**
155+
* Set start instance response.
156+
*
157+
* @param {StartInstanceResponse} startInstanceResponse
158+
*
265159
* @returns {ExecutionLogEntry[]}
266160
*/
267-
getEntries() {
161+
setStartInstanceResponse(startInstanceResponse) {
162+
this._instanceStartedData = startInstanceResponse;
163+
164+
this._updateEntries();
165+
268166
return this._entries;
269167
}
270168

271169
/**
272-
* Get entries projected for display.
170+
* Set poll response.
171+
*
172+
* @param {TaskExecutionPollData} pollResponse
273173
*
274174
* @returns {ExecutionLogEntry[]}
275175
*/
276-
getDisplayEntries() {
277-
return getDisplayEntries(this._entries);
176+
setPollResponse(pollResponse) {
177+
this._pollResponse = pollResponse;
178+
179+
this._updateEntries();
180+
181+
return this._entries;
278182
}
279183

280184
/**
281-
* Whether the log contains a terminal status (completed, incident, canceled).
282-
*
283-
* @returns {boolean}
185+
* @returns {ExecutionLogEntry[]}
284186
*/
285-
isFinished() {
286-
return isFinished(this._entries);
187+
getEntries() {
188+
return this._entries;
287189
}
288190

289191
/**
290-
* Clear all entries.
192+
* Clear all data and entries.
291193
*/
292194
reset() {
293-
this._statusEntries = [];
294-
this._pollEntriesByType = createEmptyPollEntriesByType();
195+
this._state = null;
196+
this._deployResponse = null;
197+
this._startInstanceResponse = null;
198+
this._pollResponse = null;
295199
this._entries = [];
296200
}
297201

298202
/**
299203
* Update merged entries from status history + poll snapshots.
300204
*/
301205
_updateEntries() {
302-
const pollEntries = SNAPSHOT_ENTRY_TYPES.flatMap(type => this._pollEntriesByType[type]);
303-
this._entries = [ ...this._statusEntries, ...pollEntries ];
206+
207+
// TODO: implement entire entries logic based on state and all available data (deploy, start instance, poll)
304208
}
305209

306210
/**
307211
* @param {any[]} items
308212
* @param {'job'|'user-task'|'message-subscription'} type
309-
* @param {number} pollTimestamp
310213
*
311214
* @returns {ExecutionLogEntry[]}
312215
*/
313-
_buildSimpleSliceEntries(items, type, pollTimestamp) {
216+
_buildSimpleSliceEntries(items, type) {
314217
return (items || []).map(data => ({
315218
type,
316219
data,
317-
timestamp: getTimestamp(type, data, pollTimestamp)
220+
timestamp: getTimestamp(type, data)
318221
}));
319222
}
320223

@@ -326,10 +229,10 @@ export default class ExecutionLog {
326229
* @returns {ElementInstanceEntry}
327230
*/
328231
_createElementInstanceEntry({ instance, timestamp }) {
329-
return /** @type {import('./types').ElementInstanceEntry} */ ({
330-
type: ENTRY_TYPE.ELEMENT_INSTANCE,
232+
return /** @type {import('./types').ExecutionLogElementInstanceEntry} */ ({
233+
type: EXECUTION_LOG_ENTRY_TYPE.ELEMENT_INSTANCE,
331234
data: instance,
332-
timestamp: getTimestamp(ENTRY_TYPE.ELEMENT_INSTANCE, instance, timestamp)
235+
timestamp: getTimestamp(EXECUTION_LOG_ENTRY_TYPE.ELEMENT_INSTANCE, instance, timestamp)
333236
});
334237
}
335238

0 commit comments

Comments
 (0)