@live-change/framework 0.9.171 → 0.9.173

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