@live-change/framework 0.9.171 → 0.9.174
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/lib/App.js +327 -239
- package/lib/processes/commandExecutor.js +126 -56
- package/lib/processes/eventListener.js +32 -7
- package/lib/processes/triggerExecutor.js +28 -1
- package/lib/runtime/Action.js +63 -34
- package/lib/runtime/EventHandler.js +7 -1
- package/lib/runtime/TriggerHandler.js +130 -55
- package/lib/utils.js +59 -0
- package/package.json +4 -4
- package/tsconfig.json +3 -2
package/lib/App.js
CHANGED
|
@@ -37,9 +37,12 @@ import Debug from 'debug'
|
|
|
37
37
|
|
|
38
38
|
const debug = Debug('framework')
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
import { expandObjectAttributes } from './utils.js'
|
|
41
41
|
import * as utils from './utils.js'
|
|
42
42
|
import * as validation from './utils/validation.js'
|
|
43
|
+
import { trace, SpanKind, context, propagation } from '@opentelemetry/api'
|
|
44
|
+
|
|
45
|
+
|
|
43
46
|
|
|
44
47
|
class App {
|
|
45
48
|
|
|
@@ -89,6 +92,8 @@ class App {
|
|
|
89
92
|
this.triggerRoutes = {}
|
|
90
93
|
this.globalViews = {}
|
|
91
94
|
|
|
95
|
+
this.loggingHelpers = utils.loggingHelpers('live-change/app', '')
|
|
96
|
+
this.tracer = trace.getTracer('live-change/app')
|
|
92
97
|
}
|
|
93
98
|
|
|
94
99
|
static app() {
|
|
@@ -252,205 +257,263 @@ class App {
|
|
|
252
257
|
}
|
|
253
258
|
|
|
254
259
|
async trigger(trigger, data) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
260
|
+
return this.tracer.startActiveSpan('callTrigger:'+trigger.type, {
|
|
261
|
+
kind: SpanKind.INTERNAL,
|
|
262
|
+
attributes: {
|
|
263
|
+
...expandObjectAttributes(trigger, 'trigger'),
|
|
264
|
+
...expandObjectAttributes(data, 'data'),
|
|
265
|
+
}
|
|
266
|
+
}, async (triggerSpan) => {
|
|
267
|
+
try {
|
|
268
|
+
if(!trigger) throw new Error("trigger must have type")
|
|
269
|
+
if(typeof trigger !== 'object') throw new Error("trigger must be object")
|
|
270
|
+
if(typeof trigger.type !== 'string') throw new Error("trigger type must be string")
|
|
271
|
+
if(!data) throw new Error("trigger must have data")
|
|
272
|
+
if(typeof data !== 'object') throw new Error("trigger must be object")
|
|
273
|
+
if(trigger.service) return await this.triggerService(trigger, data, true)
|
|
274
|
+
if(this.shortTriggers) {
|
|
275
|
+
const triggers = this.triggerRoutes[trigger.type] /// TODO: check if it is right
|
|
276
|
+
return await Promise.all(triggers.map(t => t.execute(data)))
|
|
277
|
+
}
|
|
278
|
+
const profileOp = await this.profileLog.begin({
|
|
279
|
+
operation: "callTrigger", triggerType: trigger.type, id: data.id, by: data.by
|
|
280
|
+
})
|
|
281
|
+
const routes = await this.dao.get(['database', 'tableRange', this.databaseName, 'triggerRoutes',
|
|
282
|
+
{ gte: trigger.type+'=', lte: trigger.type+'=\xFF\xFF\xFF\xFF' }])
|
|
283
|
+
this.loggingHelpers.log("TRIGGER ROUTES", trigger.type, '=>', routes.map(r => r.service).join(', '))
|
|
284
|
+
let promises = []
|
|
285
|
+
for(const route of routes) {
|
|
286
|
+
promises.push(this.triggerService({ ...trigger, service: route.service }, { ...data }, true))
|
|
287
|
+
}
|
|
288
|
+
const promise = Promise.all(promises)
|
|
289
|
+
await this.profileLog.endPromise(profileOp, promise)
|
|
290
|
+
const result = (await promise).flat()
|
|
291
|
+
this.loggingHelpers.log("TRIGGER FINISHED!", result)
|
|
292
|
+
return result
|
|
293
|
+
} finally {
|
|
294
|
+
triggerSpan.end()
|
|
295
|
+
}
|
|
267
296
|
})
|
|
268
|
-
const routes = await this.dao.get(['database', 'tableRange', this.databaseName, 'triggerRoutes',
|
|
269
|
-
{ gte: trigger.type+'=', lte: trigger.type+'=\xFF\xFF\xFF\xFF' }])
|
|
270
|
-
console.log("TRIGGER ROUTES", trigger.type, '=>', routes.map(r => r.service).join(', '))
|
|
271
|
-
let promises = []
|
|
272
|
-
for(const route of routes) {
|
|
273
|
-
promises.push(this.triggerService({ ...trigger, service: route.service }, { ...data }, true))
|
|
274
|
-
}
|
|
275
|
-
const promise = Promise.all(promises)
|
|
276
|
-
await this.profileLog.endPromise(profileOp, promise)
|
|
277
|
-
const result = (await promise).flat()
|
|
278
|
-
console.log("TRIGGER FINISHED!", result)
|
|
279
|
-
return result
|
|
280
297
|
}
|
|
281
298
|
|
|
282
299
|
async triggerService(trigger, data, returnArray = false) {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
300
|
+
return this.tracer.startActiveSpan('callTriggerService:'+trigger.service+'.'+trigger.type, {
|
|
301
|
+
kind: SpanKind.INTERNAL,
|
|
302
|
+
attributes: {
|
|
303
|
+
...expandObjectAttributes(trigger, 'trigger'),
|
|
304
|
+
...expandObjectAttributes(data, 'data'),
|
|
305
|
+
service: trigger.service
|
|
306
|
+
}
|
|
307
|
+
}, async (triggerSpan) => {
|
|
308
|
+
try {
|
|
309
|
+
if(!trigger.service) throw new Error("trigger must have service")
|
|
310
|
+
if(typeof trigger !== 'object') throw new Error("trigger must be object")
|
|
311
|
+
if(typeof trigger.service !== 'string') throw new Error("trigger service must be string")
|
|
312
|
+
if(typeof trigger.type !== 'string') throw new Error("trigger type must be string")
|
|
313
|
+
trigger.data = data
|
|
314
|
+
if(!trigger.data) throw new Error("trigger must have data")
|
|
315
|
+
if(typeof trigger.data !== 'object') throw new Error("trigger must be object")
|
|
316
|
+
if(this.shortTriggers) {
|
|
317
|
+
const service = this.startedServices[trigger.service]
|
|
318
|
+
const triggers = service.triggers[trigger.type]
|
|
319
|
+
if(!triggers) return []
|
|
320
|
+
const result = await Promise.all(triggers.map(t => t.execute(data)))
|
|
321
|
+
if(!returnArray && Array.isArray(result) && result.length === 1) return result[0]
|
|
322
|
+
return result
|
|
323
|
+
}
|
|
324
|
+
if(!trigger.id) trigger.id = this.generateUid()
|
|
325
|
+
trigger.state = 'new'
|
|
326
|
+
if(!trigger.timestamp) trigger.timestamp = (new Date()).toISOString()
|
|
301
327
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
328
|
+
const profileOp = await this.profileLog.begin({
|
|
329
|
+
operation: "callTriggerService", triggerType: trigger.type, service: trigger.service, triggerId: trigger.id, by: data.by
|
|
330
|
+
})
|
|
305
331
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
332
|
+
const triggersTable = this.splitCommands ? `${this.name}_triggers` : 'triggers'
|
|
333
|
+
const objectObservable = this.dao.observable(
|
|
334
|
+
['database', 'tableObject', this.databaseName, triggersTable, trigger.id],
|
|
335
|
+
ReactiveDao.ObservableValue
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
propagation.inject(context.active(), trigger._trace)
|
|
339
|
+
|
|
340
|
+
await this.dao.request(['database', 'update', this.databaseName, triggersTable, trigger.id, [{
|
|
341
|
+
op: 'conditional',
|
|
342
|
+
conditions: [{ test: 'notExist', property: 'type' }],
|
|
343
|
+
operations: [{ op: 'reverseMerge', value: trigger }],
|
|
344
|
+
}]])
|
|
345
|
+
let observer
|
|
346
|
+
const promise = new Promise((resolve, reject) => {
|
|
347
|
+
observer = (signal, value) => {
|
|
348
|
+
if(signal !== 'set') return reject('unknownSignal')
|
|
349
|
+
if(!value) return
|
|
350
|
+
if(value.state === 'done') return resolve(value.result)
|
|
351
|
+
if(value.state === 'failed') return reject(value.error)
|
|
352
|
+
}
|
|
353
|
+
objectObservable.observe(observer)
|
|
354
|
+
}).finally(() => {
|
|
355
|
+
objectObservable.unobserve(observer)
|
|
356
|
+
})
|
|
357
|
+
await this.profileLog.endPromise(profileOp, promise)
|
|
358
|
+
|
|
359
|
+
const result = await promise
|
|
360
|
+
if(!returnArray && Array.isArray(result) && result.length === 1) return result[0]
|
|
361
|
+
return result
|
|
362
|
+
} finally {
|
|
363
|
+
triggerSpan.end()
|
|
323
364
|
}
|
|
324
|
-
objectObservable.observe(observer)
|
|
325
|
-
}).finally(() => {
|
|
326
|
-
objectObservable.unobserve(observer)
|
|
327
365
|
})
|
|
328
|
-
await this.profileLog.endPromise(profileOp, promise)
|
|
329
|
-
|
|
330
|
-
const result = await promise
|
|
331
|
-
if(!returnArray && Array.isArray(result) && result.length === 1) return result[0]
|
|
332
|
-
return result
|
|
333
366
|
}
|
|
334
|
-
|
|
335
367
|
async command(data, requestTimeout) {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
368
|
+
return this.tracer.startActiveSpan('callCommand:'+data.service+'.'+data.type, {
|
|
369
|
+
kind: SpanKind.INTERNAL,
|
|
370
|
+
attributes: {
|
|
371
|
+
requestTimeout: requestTimeout,
|
|
372
|
+
...expandObjectAttributes(data, 'command'),
|
|
373
|
+
}
|
|
374
|
+
}, async (commandSpan) => {
|
|
375
|
+
const testTrace = {}
|
|
376
|
+
propagation.inject(context.active(), testTrace)
|
|
377
|
+
try {
|
|
378
|
+
|
|
379
|
+
if(!data.id) data.id = this.generateUid()
|
|
380
|
+
if(!data.service) throw new Error("command must have service")
|
|
381
|
+
if(!data.type) throw new Error("command must have type")
|
|
382
|
+
if(!data.timestamp) data.timestamp = (new Date()).toISOString()
|
|
383
|
+
data.state = 'new'
|
|
384
|
+
|
|
385
|
+
if(this.shortCommands) {
|
|
386
|
+
const command = data
|
|
387
|
+
const service = this.startedServices[data.service]
|
|
388
|
+
const action = service.actions[data.type]
|
|
389
|
+
const queuedBy = action.definition.queuedBy
|
|
390
|
+
if(queuedBy) {
|
|
391
|
+
const profileOp = await service.profileLog.begin({
|
|
392
|
+
operation: 'queueCommand', commandType: actionName,
|
|
393
|
+
commandId: command.id, client: command.client
|
|
394
|
+
})
|
|
395
|
+
const keyFunction = typeof queuedBy == 'function' ? queuedBy : (
|
|
396
|
+
Array.isArray(queuedBy) ? (c) => JSON.stringify(queuedBy.map(k => c[k])) :
|
|
397
|
+
(c) => JSON.stringify(c[queuedBy]))
|
|
398
|
+
|
|
399
|
+
const _trace = {}
|
|
400
|
+
propagation.inject(context.active(), _trace)
|
|
401
|
+
|
|
402
|
+
const routine = () => service.profileLog.profile({
|
|
403
|
+
operation: 'runCommand', commandType: actionName,
|
|
404
|
+
commandId: command.id, client: command.client
|
|
405
|
+
}, async () => {
|
|
406
|
+
propagation.extract(context.active(), _trace)
|
|
407
|
+
const reportFinished = action.definition.waitForEvents ? command.id : undefined
|
|
408
|
+
propagation.inject(context.active(), _trace)
|
|
409
|
+
const flags = {commandId: command.id, reportFinished, _trace}
|
|
410
|
+
const emit = (!this.splitEvents || this.shortEvents)
|
|
411
|
+
? new SingleEmitQueue(service, flags)
|
|
412
|
+
: new SplitEmitQueue(service, flags)
|
|
413
|
+
|
|
414
|
+
const result = await service.app.assertTime('command ' + action.definition.name,
|
|
415
|
+
action.definition.timeout || 10000,
|
|
416
|
+
() => action.runCommand(command, (...args) => emit.emit(...args)), command)
|
|
417
|
+
if(this.shortEvents) {
|
|
418
|
+
const bucket = {}
|
|
419
|
+
const eventsPromise = Promise.all(emit.emittedEvents.map(event => {
|
|
420
|
+
const service = this.startedServices[event.service]
|
|
421
|
+
const handler = service.events[event.type]
|
|
422
|
+
service.exentQueue.queue(() => handler.execute(event, bucket))
|
|
423
|
+
}))
|
|
424
|
+
if (action.definition.waitForEvents) await eventsPromise
|
|
425
|
+
} else {
|
|
426
|
+
const events = await emit.commit()
|
|
427
|
+
if (action.definition.waitForEvents)
|
|
428
|
+
await service.app.waitForEvents(reportFinished, events, action.definition.waitForEvents)
|
|
429
|
+
}
|
|
430
|
+
return result
|
|
431
|
+
})
|
|
432
|
+
routine.key = keyFunction(command)
|
|
433
|
+
const promise = service.keyBasedExecutionQueues.queue(routine)
|
|
434
|
+
await service.profileLog.endPromise(profileOp, promise)
|
|
435
|
+
return promise
|
|
376
436
|
} else {
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
437
|
+
const result = await service.app.assertTime('command ' + action.definition.name,
|
|
438
|
+
action.definition.timeout || 10000,
|
|
439
|
+
() => action.runCommand(command, (...args) => emit.emit(...args)), command)
|
|
440
|
+
if(this.shortEvents) {
|
|
441
|
+
const bucket = {}
|
|
442
|
+
console.log("emit", emit)
|
|
443
|
+
const eventsPromise = Promise.all(emit.emittedEvents.map(event => {
|
|
444
|
+
const service = this.startedServices[event.service]
|
|
445
|
+
const handler = service.events[event.type]
|
|
446
|
+
service.exentQueue.queue(() => handler.execute(event, bucket))
|
|
447
|
+
}))
|
|
448
|
+
if (action.definition.waitForEvents) await eventsPromise
|
|
449
|
+
} else {
|
|
450
|
+
const events = await emit.commit()
|
|
451
|
+
if (action.definition.waitForEvents)
|
|
452
|
+
await service.app.waitForEvents(reportFinished, events, action.definition.waitForEvents)
|
|
453
|
+
}
|
|
454
|
+
return result
|
|
380
455
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
456
|
+
} else { // queue and observe command execution
|
|
457
|
+
const profileOp = await this.profileLog.begin({
|
|
458
|
+
operation: "callCommand", commandType: data.type, service: data.service,
|
|
459
|
+
commandId: data.id, by: data.by, client: data.client
|
|
460
|
+
})
|
|
461
|
+
|
|
462
|
+
const commandsTable = this.splitCommands ? `${data.service}_commands` : 'commands'
|
|
463
|
+
const objectObservable = this.dao.observable(
|
|
464
|
+
['database', 'tableObject', this.databaseName, commandsTable, data.id],
|
|
465
|
+
ReactiveDao.ObservableValue
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
const _trace = {}
|
|
469
|
+
propagation.inject(context.active(), _trace)
|
|
470
|
+
data._trace = _trace
|
|
471
|
+
console.log("CALL COMMAND TRACE - SAVING TRACE", _trace)
|
|
472
|
+
|
|
473
|
+
await this.dao.request(['database', 'update', this.databaseName, commandsTable, data.id, [{
|
|
474
|
+
op: 'conditional',
|
|
475
|
+
conditions: [{ test: 'notExist', property: 'type' }],
|
|
476
|
+
operations: [{ op: 'reverseMerge', value: data }],
|
|
477
|
+
}]])
|
|
478
|
+
let observer
|
|
479
|
+
const promise = new Promise((resolve, reject) => {
|
|
480
|
+
observer = (signal, value) => {
|
|
481
|
+
if (signal !== 'set') return reject('unknownSignal')
|
|
482
|
+
if (!value) return
|
|
483
|
+
if (value.state === 'done') return resolve(value.result)
|
|
484
|
+
if (value.state === 'failed') return reject(value.error)
|
|
485
|
+
}
|
|
486
|
+
objectObservable.observe(observer)
|
|
487
|
+
if (!requestTimeout) {
|
|
488
|
+
requestTimeout = this.requestTimeout
|
|
489
|
+
}
|
|
490
|
+
if (requestTimeout) {
|
|
491
|
+
const timeout = setTimeout(() => {
|
|
492
|
+
this.activeTimeouts.delete(timeout)
|
|
493
|
+
reject('timeout')
|
|
494
|
+
}, requestTimeout)
|
|
495
|
+
this.activeTimeouts.add(timeout)
|
|
496
|
+
}
|
|
497
|
+
}).finally(() => {
|
|
498
|
+
objectObservable.unobserve(observer)
|
|
499
|
+
})
|
|
412
500
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
['database', 'tableObject', this.databaseName, commandsTable, data.id],
|
|
416
|
-
ReactiveDao.ObservableValue
|
|
417
|
-
)
|
|
418
|
-
await this.dao.request(['database', 'update', this.databaseName, commandsTable, data.id, [{
|
|
419
|
-
op: 'conditional',
|
|
420
|
-
conditions: [{ test: 'notExist', property: 'type' }],
|
|
421
|
-
operations: [{ op: 'reverseMerge', value: data }],
|
|
422
|
-
}]])
|
|
423
|
-
let observer
|
|
424
|
-
const promise = new Promise((resolve, reject) => {
|
|
425
|
-
observer = (signal, value) => {
|
|
426
|
-
if (signal !== 'set') return reject('unknownSignal')
|
|
427
|
-
if (!value) return
|
|
428
|
-
if (value.state === 'done') return resolve(value.result)
|
|
429
|
-
if (value.state === 'failed') return reject(value.error)
|
|
430
|
-
}
|
|
431
|
-
objectObservable.observe(observer)
|
|
432
|
-
if (!requestTimeout) {
|
|
433
|
-
requestTimeout = this.requestTimeout
|
|
501
|
+
await this.profileLog.endPromise(profileOp, promise)
|
|
502
|
+
return promise
|
|
434
503
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
}, requestTimeout)
|
|
440
|
-
this.activeTimeouts.add(timeout)
|
|
441
|
-
}
|
|
442
|
-
}).finally(() => {
|
|
443
|
-
objectObservable.unobserve(observer)
|
|
444
|
-
})
|
|
445
|
-
|
|
446
|
-
await this.profileLog.endPromise(profileOp, promise)
|
|
447
|
-
return promise
|
|
448
|
-
}
|
|
504
|
+
} finally {
|
|
505
|
+
commandSpan.end()
|
|
506
|
+
}
|
|
507
|
+
})
|
|
449
508
|
}
|
|
450
509
|
|
|
451
510
|
async emitEvents(service, events, flags = {}) {
|
|
452
511
|
for(let event of events) {
|
|
453
512
|
if(!event.service) event.service = service
|
|
513
|
+
if(!event._trace) {
|
|
514
|
+
event._trace = {}
|
|
515
|
+
propagation.inject(context.active(), event._trace)
|
|
516
|
+
}
|
|
454
517
|
}
|
|
455
518
|
if(this.splitEvents) {
|
|
456
519
|
let promises = []
|
|
@@ -476,73 +539,97 @@ class App {
|
|
|
476
539
|
|
|
477
540
|
async waitForEvents(reportId, events, timeout) {
|
|
478
541
|
if(events.length === 0) {
|
|
479
|
-
|
|
542
|
+
this.loggingHelpers.log("no events, no need to wait", reportId)
|
|
480
543
|
return
|
|
481
544
|
}
|
|
482
545
|
const [action, id] = reportId.split('_')
|
|
483
546
|
const commandId = id
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
const eventsNotDone = events.filter(event => finished.find(e => e.id === event.id))
|
|
493
|
-
if(eventsNotDone.length > 0) {
|
|
494
|
-
console.error(" pending events:")
|
|
495
|
-
for(const event of eventsNotDone) {
|
|
496
|
-
console.error(` ${event.id} - type: ${event.type}`)
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
reject(message)
|
|
500
|
-
done = true
|
|
547
|
+
|
|
548
|
+
return this.tracer.startActiveSpan('waitForEvents', {
|
|
549
|
+
kind: SpanKind.INTERNAL,
|
|
550
|
+
attributes: {
|
|
551
|
+
reportId: reportId,
|
|
552
|
+
eventTypes: events.map(e => e.type),
|
|
553
|
+
eventIds: events.map(e => e.id),
|
|
554
|
+
timeout: timeout
|
|
501
555
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
556
|
+
}, async (waitForEventsSpan) => {
|
|
557
|
+
const profileOp = await this.profileLog.begin({
|
|
558
|
+
operation: "waitForEvents", action: action, commandId, reportId, events, timeout
|
|
559
|
+
})
|
|
560
|
+
const _trace = {}
|
|
561
|
+
propagation.inject(context.active(), _trace)
|
|
562
|
+
const promise = new Promise((resolve, reject) => {
|
|
563
|
+
propagation.extract(context.active(), _trace)
|
|
564
|
+
let done = false
|
|
565
|
+
let finishedEvents = []
|
|
566
|
+
const handleError = (message) => {
|
|
567
|
+
let errorMessage = `waitForEvents error: ${message}`
|
|
568
|
+
const eventsNotDone = events.filter(event => finishedEvents.find(e => e.id === event.id))
|
|
569
|
+
if(eventsNotDone.length > 0) {
|
|
570
|
+
errorMessage += "\n pending events:"
|
|
571
|
+
for(const event of eventsNotDone) {
|
|
572
|
+
errorMessage += `\n ${event.id} - type: ${event.type}`
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
this.loggingHelpers.error(errorMessage)
|
|
576
|
+
reject(message)
|
|
577
|
+
done = true
|
|
508
578
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
579
|
+
const observable = this.dao.observable(
|
|
580
|
+
['database', 'tableObject', this.databaseName, 'eventReports', reportId]
|
|
581
|
+
)
|
|
582
|
+
const reportsObserver = (signal, data) => {
|
|
583
|
+
if(signal !== 'set') {
|
|
584
|
+
handleError(`unknown signal ${signal} with data: ${data}`)
|
|
585
|
+
}
|
|
586
|
+
if(data == null) return /// wait for real data
|
|
587
|
+
if(data.finished) {
|
|
588
|
+
finishedEvents = data.finished
|
|
589
|
+
if(finishedEvents.length >= events.length) {
|
|
590
|
+
const eventsNotDone = events.filter(event => data.finished.find(e => e.id === event.id))
|
|
591
|
+
if(eventsNotDone.length !== 0) {
|
|
592
|
+
const eventsDone = events.filter(event => !data.finished.find(e => e.id === event.id))
|
|
593
|
+
let errorMessage = "waitForEvents - finished events does not match!"
|
|
594
|
+
errorMessage += "\n finished events:"
|
|
595
|
+
for(const event of eventsDone) {
|
|
596
|
+
errorMessage += `\n ${event.id} - type: ${event.type}`
|
|
597
|
+
}
|
|
598
|
+
errorMessage += "\n pending events:"
|
|
599
|
+
for(const event of eventsNotDone) {
|
|
600
|
+
errorMessage += `\n ${event.id} - type: ${event.type}`
|
|
601
|
+
}
|
|
602
|
+
this.loggingHelpers.error(errorMessage)
|
|
603
|
+
} else {
|
|
604
|
+
this.loggingHelpers.log("waiting for events finished", reportId)
|
|
605
|
+
resolve('finished')
|
|
606
|
+
observable.unobserve(reportsObserver)
|
|
524
607
|
}
|
|
525
|
-
} else {
|
|
526
|
-
console.log("waiting for events finished", reportId)
|
|
527
|
-
resolve('finished')
|
|
528
|
-
observable.unobserve(reportsObserver)
|
|
529
608
|
}
|
|
530
609
|
}
|
|
531
610
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
611
|
+
this.loggingHelpers.log("waiting for events", reportId)
|
|
612
|
+
observable.observe(reportsObserver)
|
|
613
|
+
if(Number.isFinite(timeout)) {
|
|
614
|
+
setTimeout(() => {
|
|
615
|
+
if(done) return
|
|
616
|
+
observable.unobserve(reportsObserver)
|
|
617
|
+
this.loggingHelpers.error("events timeout", reportId)
|
|
618
|
+
handleError('timeout')
|
|
619
|
+
}, timeout)
|
|
620
|
+
}
|
|
621
|
+
})
|
|
622
|
+
await this.profileLog.endPromise(profileOp, promise)
|
|
623
|
+
promise.then(() => {
|
|
624
|
+
waitForEventsSpan.end()
|
|
625
|
+
})
|
|
626
|
+
promise.catch((error) => {
|
|
627
|
+
this.loggingHelpers.error("waitForEvents error", error)
|
|
628
|
+
waitForEventsSpan.end()
|
|
629
|
+
throw error
|
|
630
|
+
})
|
|
631
|
+
return promise
|
|
632
|
+
})
|
|
546
633
|
}
|
|
547
634
|
|
|
548
635
|
async emitEventsAndWait(service, events, flags = {}) {
|
|
@@ -554,7 +641,8 @@ class App {
|
|
|
554
641
|
async assertTime(taskName, duration, task, ...data) {
|
|
555
642
|
const profileOp = await this.profileLog.begin({ operation: 'assertTime', taskName })
|
|
556
643
|
const taskTimeout = setTimeout(() => {
|
|
557
|
-
|
|
644
|
+
this.loggingHelpers.error(`TASK ${taskName} TIMEOUT`, ...data)
|
|
645
|
+
//console.log(`TASK ${taskName} TIMEOUT`, ...data)
|
|
558
646
|
this.profileLog.end({ ...profileOp, result: "timeout" })
|
|
559
647
|
}, duration)
|
|
560
648
|
try {
|