@live-change/task-service 0.9.106 → 0.9.107
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/model.js +32 -1
- package/package.json +4 -4
- package/task.ts +110 -70
package/model.js
CHANGED
|
@@ -19,6 +19,9 @@ const taskProperties = {
|
|
|
19
19
|
properties: {
|
|
20
20
|
type: Object
|
|
21
21
|
},
|
|
22
|
+
client: {
|
|
23
|
+
type: Object,
|
|
24
|
+
},
|
|
22
25
|
result: {
|
|
23
26
|
type: Object
|
|
24
27
|
},
|
|
@@ -96,6 +99,9 @@ const Task = definition.model({
|
|
|
96
99
|
byCauseAndState: {
|
|
97
100
|
property: ['causeType', 'cause', 'state']
|
|
98
101
|
},
|
|
102
|
+
byCauseAndStart: {
|
|
103
|
+
property: ['causeType', 'cause', 'startedAt']
|
|
104
|
+
},
|
|
99
105
|
byState: {
|
|
100
106
|
property: ['state']
|
|
101
107
|
},
|
|
@@ -182,7 +188,7 @@ const Task = definition.model({
|
|
|
182
188
|
function: async function(input, output, { tableName }) {
|
|
183
189
|
function mapFunction(obj) {
|
|
184
190
|
if(!obj) return null
|
|
185
|
-
if(['done', 'failed', 'canceled'].includes(obj.state)) return null
|
|
191
|
+
if(['done', 'failed', 'canceled', 'fallbackDone'].includes(obj.state)) return null
|
|
186
192
|
if(obj.causeType === tableName) return null
|
|
187
193
|
return { id: `"${obj.name}"_${obj.id}`, to: obj.id }
|
|
188
194
|
}
|
|
@@ -259,6 +265,31 @@ definition.view({
|
|
|
259
265
|
}
|
|
260
266
|
})
|
|
261
267
|
|
|
268
|
+
definition.view({
|
|
269
|
+
name: 'tasksByCauseAndStart',
|
|
270
|
+
internal: true,
|
|
271
|
+
properties: {
|
|
272
|
+
causeType: {
|
|
273
|
+
type: String
|
|
274
|
+
},
|
|
275
|
+
cause: {
|
|
276
|
+
type: String
|
|
277
|
+
},
|
|
278
|
+
...App.rangeProperties
|
|
279
|
+
},
|
|
280
|
+
returns: {
|
|
281
|
+
type: Array,
|
|
282
|
+
of: {
|
|
283
|
+
type: Task
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
async daoPath(props) {
|
|
287
|
+
const { causeType, cause } = props
|
|
288
|
+
const range = App.extractRange(props)
|
|
289
|
+
return Task.sortedIndexRangePath('byCauseAndStart', [causeType, cause], range)
|
|
290
|
+
}
|
|
291
|
+
})
|
|
292
|
+
|
|
262
293
|
definition.view({
|
|
263
294
|
name: 'tasksByRoot',
|
|
264
295
|
properties: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@live-change/task-service",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.107",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
},
|
|
23
23
|
"type": "module",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@live-change/framework": "^0.9.
|
|
26
|
-
"@live-change/relations-plugin": "^0.9.
|
|
25
|
+
"@live-change/framework": "^0.9.107",
|
|
26
|
+
"@live-change/relations-plugin": "^0.9.107"
|
|
27
27
|
},
|
|
28
|
-
"gitHead": "
|
|
28
|
+
"gitHead": "c546be0c12841aa8eca060d4c53d6d89bac2d088"
|
|
29
29
|
}
|
package/task.ts
CHANGED
|
@@ -36,11 +36,17 @@ async function triggerOnTaskStateChange(taskObject, causeType, cause) {
|
|
|
36
36
|
})
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
interface ClientInfo {
|
|
40
|
+
user?: string
|
|
41
|
+
session?: string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function createOrReuseTask(taskDefinition, props, causeType, cause, expire, client:ClientInfo) {
|
|
40
45
|
const propertiesJson = JSON.stringify(props)
|
|
46
|
+
const userInfo = client?.user ? `user:${client.user}` : `session:${client?.session}`
|
|
41
47
|
const hash = crypto
|
|
42
48
|
.createHash('sha256')
|
|
43
|
-
.update(taskDefinition.name + ':' + propertiesJson)
|
|
49
|
+
.update(taskDefinition.name + ':' + propertiesJson + userInfo)
|
|
44
50
|
.digest('hex')
|
|
45
51
|
|
|
46
52
|
const expireDate = (expire ?? taskDefinition.expire) ? new Date(Date.now() - taskDefinition.expire) : null
|
|
@@ -66,7 +72,8 @@ async function createOrReuseTask(taskDefinition, props, causeType, cause, expire
|
|
|
66
72
|
state: 'created',
|
|
67
73
|
service: taskDefinition.service,
|
|
68
74
|
retries: [],
|
|
69
|
-
maxRetries: taskDefinition.maxRetries ?? 5
|
|
75
|
+
maxRetries: taskDefinition.maxRetries ?? 5,
|
|
76
|
+
client
|
|
70
77
|
}
|
|
71
78
|
|
|
72
79
|
if(!oldTask) {
|
|
@@ -82,12 +89,13 @@ async function createOrReuseTask(taskDefinition, props, causeType, cause, expire
|
|
|
82
89
|
return taskObject
|
|
83
90
|
}
|
|
84
91
|
|
|
85
|
-
async function startTask(taskFunction, props, causeType, cause, expire){
|
|
86
|
-
const taskObject = await createOrReuseTask(taskFunction.definition, props, causeType, cause, expire)
|
|
92
|
+
async function startTask(taskFunction, props, causeType, cause, expire, client:ClientInfo){
|
|
93
|
+
const taskObject = await createOrReuseTask(taskFunction.definition, props, causeType, cause, expire, client)
|
|
87
94
|
const context = {
|
|
88
95
|
causeType,
|
|
89
96
|
cause,
|
|
90
97
|
taskObject,
|
|
98
|
+
client
|
|
91
99
|
}
|
|
92
100
|
console.log("START TASK!", taskFunction.name)
|
|
93
101
|
const promise = taskFunction(props, context)
|
|
@@ -181,6 +189,34 @@ interface TaskDefinition {
|
|
|
181
189
|
|
|
182
190
|
}
|
|
183
191
|
|
|
192
|
+
function progressCounter(reportProgress) {
|
|
193
|
+
let currentAcc = 0
|
|
194
|
+
let totalAcc = 0
|
|
195
|
+
const progressFunction = (current, total, action, opts) => {
|
|
196
|
+
currentAcc = current
|
|
197
|
+
totalAcc = total
|
|
198
|
+
reportProgress(currentAcc, totalAcc, action, opts)
|
|
199
|
+
}
|
|
200
|
+
progressFunction.increment = (action, by = 1, opts) => {
|
|
201
|
+
currentAcc += by
|
|
202
|
+
progressFunction(currentAcc, totalAcc, action, opts)
|
|
203
|
+
}
|
|
204
|
+
progressFunction.incrementTotal = (action, by = 1, opts) => {
|
|
205
|
+
totalAcc += by
|
|
206
|
+
progressFunction(currentAcc, totalAcc, action, opts)
|
|
207
|
+
}
|
|
208
|
+
progressFunction.slice = (sliceSize, factor = 1.0) => {
|
|
209
|
+
const sliceStart = currentAcc
|
|
210
|
+
const sliceEnd = sliceStart + sliceSize
|
|
211
|
+
currentAcc = sliceEnd
|
|
212
|
+
return progressCounter((current, total, action, opts) => {
|
|
213
|
+
progressFunction(sliceStart + Math.min(current, sliceSize) * factor,
|
|
214
|
+
sliceEnd + Math.min(total, sliceSize) * factor, action, opts)
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
return progressFunction
|
|
218
|
+
}
|
|
219
|
+
|
|
184
220
|
type TaskFunction = (props, context: TaskExecuteContext, emit, reportProgress) => Promise<any>
|
|
185
221
|
|
|
186
222
|
export default function task(definition:TaskDefinition, serviceDefinition) {
|
|
@@ -196,7 +232,7 @@ export default function task(definition:TaskDefinition, serviceDefinition) {
|
|
|
196
232
|
}
|
|
197
233
|
|
|
198
234
|
let taskObject = context.taskObject ??
|
|
199
|
-
await createOrReuseTask(definition, props, context.causeType, context.cause, context.expire)
|
|
235
|
+
await createOrReuseTask(definition, props, context.causeType, context.cause, context.expire, context.client)
|
|
200
236
|
|
|
201
237
|
if(!taskObject?.state) throw new Error('Task object state is not defined in ' + taskObject)
|
|
202
238
|
if(!taskObject?.id) throw new Error('Task object id is not defined in '+taskObject)
|
|
@@ -258,60 +294,65 @@ export default function task(definition:TaskDefinition, serviceDefinition) {
|
|
|
258
294
|
startedAt: new Date()
|
|
259
295
|
})
|
|
260
296
|
await triggerOnTaskStateChange(taskObject, context.causeType, context.cause)
|
|
297
|
+
const commonFunctions = {
|
|
298
|
+
async run(taskFunction: TaskFunction, props, progressFactor = 1, expire = undefined) {
|
|
299
|
+
if(typeof taskFunction !== 'function') {
|
|
300
|
+
console.log("TASK FUNCTION", taskFunction)
|
|
301
|
+
throw new Error('Task function is not a function')
|
|
302
|
+
}
|
|
303
|
+
//console.log("SUBTASK RUN", taskFunction.definition.name, props)
|
|
304
|
+
const subtaskProgress = { current: 0, total: 1, factor: progressFactor }
|
|
305
|
+
subtasksProgress.push(subtaskProgress)
|
|
306
|
+
try {
|
|
307
|
+
const result = await taskFunction(
|
|
308
|
+
props,
|
|
309
|
+
{
|
|
310
|
+
...context,
|
|
311
|
+
taskObject: undefined,
|
|
312
|
+
causeType: 'task_Task',
|
|
313
|
+
cause: taskObject.id,
|
|
314
|
+
expire
|
|
315
|
+
},
|
|
316
|
+
(events) => app.emitEvents(serviceDefinition.name,
|
|
317
|
+
Array.isArray(events) ? events : [events], {}),
|
|
318
|
+
(current, total, action) => {
|
|
319
|
+
subtaskProgress.current = current
|
|
320
|
+
subtaskProgress.total = total
|
|
321
|
+
updateProgress()
|
|
322
|
+
}
|
|
323
|
+
)
|
|
324
|
+
//console.log("SUBTASK DONE", taskFunction.definition.name, props, '=>', result)
|
|
325
|
+
subtaskProgress.current = subtaskProgress.total
|
|
326
|
+
updateProgress()
|
|
327
|
+
return result
|
|
328
|
+
} catch(error) {
|
|
329
|
+
subtaskProgress.current = subtaskProgress.total // failed = finished
|
|
330
|
+
const outputError: any = new Error("Subtask error: " + error.toString())
|
|
331
|
+
outputError.stack = error.stack
|
|
332
|
+
outputError.taskNoRetry = true
|
|
333
|
+
throw outputError
|
|
334
|
+
}
|
|
335
|
+
},
|
|
336
|
+
progress: progressCounter((current, total, action, opts) => { // throttle this
|
|
337
|
+
selfProgress = {
|
|
338
|
+
...opts,
|
|
339
|
+
current, total, action
|
|
340
|
+
}
|
|
341
|
+
updateProgress()
|
|
342
|
+
})
|
|
343
|
+
}
|
|
261
344
|
const runContext = {
|
|
262
345
|
...context,
|
|
263
346
|
task: {
|
|
264
347
|
id: taskObject.id,
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
throw new Error('Task function is not a function')
|
|
269
|
-
}
|
|
270
|
-
//console.log("SUBTASK RUN", taskFunction.definition.name, props)
|
|
271
|
-
const subtaskProgress = { current: 0, total: 1, factor: progressFactor }
|
|
272
|
-
subtasksProgress.push(subtaskProgress)
|
|
273
|
-
try {
|
|
274
|
-
const result = await taskFunction(
|
|
275
|
-
props,
|
|
276
|
-
{
|
|
277
|
-
...context,
|
|
278
|
-
taskObject: undefined,
|
|
279
|
-
causeType: 'task_Task',
|
|
280
|
-
cause: taskObject.id,
|
|
281
|
-
expire
|
|
282
|
-
},
|
|
283
|
-
(events) => app.emitEvents(serviceDefinition.name,
|
|
284
|
-
Array.isArray(events) ? events : [events], {}),
|
|
285
|
-
(current, total, action) => {
|
|
286
|
-
subtaskProgress.current = current
|
|
287
|
-
subtaskProgress.total = total
|
|
288
|
-
updateProgress()
|
|
289
|
-
}
|
|
290
|
-
)
|
|
291
|
-
//console.log("SUBTASK DONE", taskFunction.definition.name, props, '=>', result)
|
|
292
|
-
subtaskProgress.current = subtaskProgress.total
|
|
293
|
-
updateProgress()
|
|
294
|
-
return result
|
|
295
|
-
} catch(error) {
|
|
296
|
-
subtaskProgress.current = subtaskProgress.total // failed = finished
|
|
297
|
-
const outputError: any = new Error("Subtask error: " + error.toString())
|
|
298
|
-
outputError.stack = error.stack
|
|
299
|
-
outputError.taskNoRetry = true
|
|
300
|
-
throw outputError
|
|
301
|
-
}
|
|
302
|
-
},
|
|
303
|
-
async progress(current, total, action, opts) { // throttle this
|
|
304
|
-
selfProgress = {
|
|
305
|
-
...opts,
|
|
306
|
-
current, total, action
|
|
307
|
-
}
|
|
308
|
-
updateProgress()
|
|
309
|
-
}
|
|
310
|
-
},
|
|
348
|
+
...commonFunctions
|
|
349
|
+
},
|
|
350
|
+
...commonFunctions,
|
|
311
351
|
async trigger(trigger, props) {
|
|
312
352
|
return await app.trigger({
|
|
313
353
|
causeType: 'task_Task',
|
|
314
354
|
cause: taskObject.id,
|
|
355
|
+
client: context.client,
|
|
315
356
|
...trigger,
|
|
316
357
|
}, props)
|
|
317
358
|
},
|
|
@@ -319,6 +360,7 @@ export default function task(definition:TaskDefinition, serviceDefinition) {
|
|
|
319
360
|
return await app.triggerService({
|
|
320
361
|
causeType: 'task_Task',
|
|
321
362
|
cause: taskObject.id,
|
|
363
|
+
client: context.client,
|
|
322
364
|
...trigger
|
|
323
365
|
}, props, returnArray)
|
|
324
366
|
},
|
|
@@ -326,6 +368,7 @@ export default function task(definition:TaskDefinition, serviceDefinition) {
|
|
|
326
368
|
const tasks = await app.trigger({
|
|
327
369
|
causeType: 'task_Task',
|
|
328
370
|
cause: taskObject.id,
|
|
371
|
+
client: context.client,
|
|
329
372
|
...trigger
|
|
330
373
|
}, data)
|
|
331
374
|
const fullProgressSum = tasks.length * progressFactor
|
|
@@ -374,7 +417,7 @@ export default function task(definition:TaskDefinition, serviceDefinition) {
|
|
|
374
417
|
//console.log("TASK WATCHERS PROMISES FULLFILLED", taskWatchers)
|
|
375
418
|
const results = taskWatchers.map(watcher => {
|
|
376
419
|
//console.log("WATCHER OBSERVABLE", watcher.observable)
|
|
377
|
-
watcher.observable.getValue().result
|
|
420
|
+
return watcher.observable.getValue().result
|
|
378
421
|
})
|
|
379
422
|
return results
|
|
380
423
|
} catch(subtask) {
|
|
@@ -447,17 +490,17 @@ export default function task(definition:TaskDefinition, serviceDefinition) {
|
|
|
447
490
|
}
|
|
448
491
|
}
|
|
449
492
|
|
|
450
|
-
|
|
493
|
+
if(taskObject.state === 'failed') {
|
|
494
|
+
throw new Error(taskObject.retries[taskObject.retries.length - 1].error)
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
while(taskObject.state !== 'done' && taskObject.state !== 'fallbackDone' && taskObject.state !== 'failed') {
|
|
451
498
|
console.log("RUNNING TASK", definition.name, "STATE", taskObject.state, "OBJECT", taskObject)
|
|
452
499
|
await runTask()
|
|
453
500
|
console.log("TASK", definition.name, "AFTER RUNTASK", taskObject)
|
|
454
501
|
// console.log("TASK", definition.name, "AFTER RUNTASK", taskObject)
|
|
455
502
|
}
|
|
456
503
|
|
|
457
|
-
if(taskObject.state === 'failed') {
|
|
458
|
-
throw new Error(taskObject.retries[taskObject.retries.length - 1].error)
|
|
459
|
-
}
|
|
460
|
-
|
|
461
504
|
return taskObject.result
|
|
462
505
|
}
|
|
463
506
|
|
|
@@ -481,6 +524,7 @@ export default function task(definition:TaskDefinition, serviceDefinition) {
|
|
|
481
524
|
causeType: task.causeType,
|
|
482
525
|
cause: task.cause,
|
|
483
526
|
taskObject,
|
|
527
|
+
client: task.client
|
|
484
528
|
}
|
|
485
529
|
/// mark started subtasks as interrupted
|
|
486
530
|
const subtasks = await app.serviceViewGet('task', 'tasksByRoot', {
|
|
@@ -526,10 +570,10 @@ export default function task(definition:TaskDefinition, serviceDefinition) {
|
|
|
526
570
|
},
|
|
527
571
|
async execute(props, context, emit) {
|
|
528
572
|
const startResult =
|
|
529
|
-
await startTask(taskFunction, props,
|
|
573
|
+
await startTask(taskFunction, props,
|
|
530
574
|
context.reaction.causeType ?? 'trigger',
|
|
531
575
|
context.reaction.cause ?? context.reaction.id,
|
|
532
|
-
props.taskExpire) // Must be done that way, so subtasks can be connected to the parent task
|
|
576
|
+
props.taskExpire, context.client) // Must be done that way, so subtasks can be connected to the parent task
|
|
533
577
|
return startResult.task
|
|
534
578
|
}
|
|
535
579
|
})
|
|
@@ -543,10 +587,11 @@ export default function task(definition:TaskDefinition, serviceDefinition) {
|
|
|
543
587
|
},
|
|
544
588
|
async execute(props, context, emit) {
|
|
545
589
|
const startResult =
|
|
546
|
-
await startTask(taskFunction,
|
|
590
|
+
await startTask(taskFunction,
|
|
591
|
+
props,
|
|
547
592
|
context.reaction.causeType ?? 'trigger',
|
|
548
593
|
context.reaction.cause ?? context.reaction.id,
|
|
549
|
-
props.taskExpire) // Must be done that way, so subtasks can be connected to the parent task
|
|
594
|
+
props.taskExpire, context.client) // Must be done that way, so subtasks can be connected to the parent task
|
|
550
595
|
return startResult.task
|
|
551
596
|
}
|
|
552
597
|
})
|
|
@@ -562,23 +607,18 @@ export default function task(definition:TaskDefinition, serviceDefinition) {
|
|
|
562
607
|
type: Task
|
|
563
608
|
},
|
|
564
609
|
async execute(props, context, emit) {
|
|
565
|
-
const client = {
|
|
566
|
-
...context.client,
|
|
567
|
-
sessionKey: undefined
|
|
568
|
-
}
|
|
569
610
|
const startResult =
|
|
570
|
-
await startTask(taskFunction,
|
|
611
|
+
await startTask(taskFunction, props, 'command', context.command.id, undefined, context.client)
|
|
571
612
|
return startResult.task
|
|
572
613
|
},
|
|
573
614
|
...(typeof definition.action === 'object' && definition.action)
|
|
574
615
|
})
|
|
575
616
|
|
|
576
|
-
|
|
577
617
|
}
|
|
578
618
|
|
|
579
619
|
taskFunction.definition = definition
|
|
580
|
-
taskFunction.start = async (props, causeType, cause, expire = undefined) => {
|
|
581
|
-
return await startTask(taskFunction, props, causeType, cause, expire)
|
|
620
|
+
taskFunction.start = async (props, causeType, cause, expire = undefined, client:ClientInfo) => {
|
|
621
|
+
return await startTask(taskFunction, props, causeType, cause, expire, client)
|
|
582
622
|
}
|
|
583
623
|
return taskFunction
|
|
584
624
|
|