• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

typeorm / typeorm / 15219332477

23 May 2025 09:13PM UTC coverage: 17.216% (-59.1%) from 76.346%
15219332477

Pull #11332

github

naorpeled
cr comments - move if block
Pull Request #11332: feat: add new undefined and null behavior flags

1603 of 12759 branches covered (12.56%)

Branch coverage included in aggregate %.

0 of 31 new or added lines in 3 files covered. (0.0%)

14132 existing lines in 166 files now uncovered.

4731 of 24033 relevant lines covered (19.69%)

60.22 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

29.18
/src/subscriber/Broadcaster.ts
1
import { ObjectLiteral } from "../common/ObjectLiteral"
2
import { ColumnMetadata } from "../metadata/ColumnMetadata"
3
import { EntityMetadata } from "../metadata/EntityMetadata"
4
import { RelationMetadata } from "../metadata/RelationMetadata"
5
import { QueryRunner } from "../query-runner/QueryRunner"
6
import { ObjectUtils } from "../util/ObjectUtils"
1✔
7
import { BroadcasterResult } from "./BroadcasterResult"
1✔
8
import { EntitySubscriberInterface } from "./EntitySubscriberInterface"
9

10
interface BroadcasterEvents {
11
    BeforeQuery: (query: string, parameters: any[] | undefined) => void
12
    AfterQuery: (
13
        query: string,
14
        parameters: any[] | undefined,
15
        success: boolean,
16
        executionTime: number | undefined,
17
        rawResults: any | undefined,
18
        error: any | undefined,
19
    ) => void
20

21
    BeforeTransactionCommit: () => void
22
    AfterTransactionCommit: () => void
23
    BeforeTransactionStart: () => void
24
    AfterTransactionStart: () => void
25
    BeforeTransactionRollback: () => void
26
    AfterTransactionRollback: () => void
27

28
    BeforeUpdate: (
29
        metadata: EntityMetadata,
30
        entity?: ObjectLiteral,
31
        databaseEntity?: ObjectLiteral,
32
        updatedColumns?: ColumnMetadata[],
33
        updatedRelations?: RelationMetadata[],
34
    ) => void
35
    AfterUpdate: (
36
        metadata: EntityMetadata,
37
        entity?: ObjectLiteral,
38
        databaseEntity?: ObjectLiteral,
39
        updatedColumns?: ColumnMetadata[],
40
        updatedRelations?: RelationMetadata[],
41
    ) => void
42

43
    BeforeInsert: (
44
        metadata: EntityMetadata,
45
        entity: ObjectLiteral | undefined,
46
    ) => void
47
    AfterInsert: (
48
        metadata: EntityMetadata,
49
        entity: ObjectLiteral | undefined,
50
    ) => void
51

52
    BeforeRemove: (
53
        metadata: EntityMetadata,
54
        entity?: ObjectLiteral,
55
        databaseEntity?: ObjectLiteral,
56
    ) => void
57
    AfterRemove: (
58
        metadata: EntityMetadata,
59
        entity?: ObjectLiteral,
60
        databaseEntity?: ObjectLiteral,
61
    ) => void
62

63
    BeforeSoftRemove: (
64
        metadata: EntityMetadata,
65
        entity?: ObjectLiteral,
66
        databaseEntity?: ObjectLiteral,
67
    ) => void
68
    AfterSoftRemove: (
69
        metadata: EntityMetadata,
70
        entity?: ObjectLiteral,
71
        databaseEntity?: ObjectLiteral,
72
    ) => void
73

74
    BeforeRecover: (
75
        metadata: EntityMetadata,
76
        entity?: ObjectLiteral,
77
        databaseEntity?: ObjectLiteral,
78
    ) => void
79
    AfterRecover: (
80
        metadata: EntityMetadata,
81
        entity?: ObjectLiteral,
82
        databaseEntity?: ObjectLiteral,
83
    ) => void
84

85
    Load: (metadata: EntityMetadata, entities: ObjectLiteral[]) => void
86
}
87

88
/**
89
 * Broadcaster provides a helper methods to broadcast events to the subscribers.
90
 */
91
export class Broadcaster {
1✔
92
    // -------------------------------------------------------------------------
93
    // Constructor
94
    // -------------------------------------------------------------------------
95

96
    constructor(private queryRunner: QueryRunner) {}
31✔
97

98
    // -------------------------------------------------------------------------
99
    // Public Methods
100
    // -------------------------------------------------------------------------
101

102
    async broadcast<U extends keyof BroadcasterEvents>(
103
        event: U,
104
        ...args: Parameters<BroadcasterEvents[U]>
105
    ): Promise<void> {
106
        const result = new BroadcasterResult()
176✔
107

108
        const broadcastFunction = this[`broadcast${event}Event` as keyof this]
176✔
109

110
        if (typeof broadcastFunction === "function") {
176✔
111
            ;(broadcastFunction as any).call(this, result, ...args)
176✔
112
        }
113

114
        await result.wait()
176✔
115
    }
116

117
    /**
118
     * Broadcasts "BEFORE_INSERT" event.
119
     * Before insert event is executed before entity is being inserted to the database for the first time.
120
     * All subscribers and entity listeners who listened to this event will be executed at this point.
121
     * Subscribers and entity listeners can return promises, it will wait until they are resolved.
122
     *
123
     * Note: this method has a performance-optimized code organization, do not change code structure.
124
     */
125
    broadcastBeforeInsertEvent(
126
        result: BroadcasterResult,
127
        metadata: EntityMetadata,
128
        entity: undefined | ObjectLiteral,
129
    ): void {
130
        if (entity && metadata.beforeInsertListeners.length) {
252✔
131
            metadata.beforeInsertListeners.forEach((listener) => {
3✔
132
                if (listener.isAllowed(entity)) {
9✔
133
                    const executionResult = listener.execute(entity)
9✔
134
                    if (executionResult instanceof Promise)
9!
135
                        result.promises.push(executionResult)
×
136
                    result.count++
9✔
137
                }
138
            })
139
        }
140

141
        if (this.queryRunner.connection.subscribers.length) {
252✔
142
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
2✔
143
                if (
2!
144
                    this.isAllowedSubscriber(subscriber, metadata.target) &&
4✔
145
                    subscriber.beforeInsert
146
                ) {
UNCOV
147
                    const executionResult = subscriber.beforeInsert({
×
148
                        connection: this.queryRunner.connection,
149
                        queryRunner: this.queryRunner,
150
                        manager: this.queryRunner.manager,
151
                        entity: entity,
152
                        metadata: metadata,
153
                    })
UNCOV
154
                    if (executionResult instanceof Promise)
×
UNCOV
155
                        result.promises.push(executionResult)
×
UNCOV
156
                    result.count++
×
157
                }
158
            })
159
        }
160
    }
161

162
    /**
163
     * Broadcasts "BEFORE_UPDATE" event.
164
     * Before update event is executed before entity is being updated in the database.
165
     * All subscribers and entity listeners who listened to this event will be executed at this point.
166
     * Subscribers and entity listeners can return promises, it will wait until they are resolved.
167
     *
168
     * Note: this method has a performance-optimized code organization, do not change code structure.
169
     */
170
    broadcastBeforeUpdateEvent(
171
        result: BroadcasterResult,
172
        metadata: EntityMetadata,
173
        entity?: ObjectLiteral,
174
        databaseEntity?: ObjectLiteral,
175
        updatedColumns?: ColumnMetadata[],
176
        updatedRelations?: RelationMetadata[],
177
    ): void {
178
        // todo: send relations too?
179
        if (entity && metadata.beforeUpdateListeners.length) {
12✔
180
            metadata.beforeUpdateListeners.forEach((listener) => {
3✔
181
                if (listener.isAllowed(entity)) {
3✔
182
                    const executionResult = listener.execute(entity)
3✔
183
                    if (executionResult instanceof Promise)
3✔
184
                        result.promises.push(executionResult)
3✔
185
                    result.count++
3✔
186
                }
187
            })
188
        }
189

190
        if (this.queryRunner.connection.subscribers.length) {
12✔
191
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
3✔
192
                if (
3✔
193
                    this.isAllowedSubscriber(subscriber, metadata.target) &&
6✔
194
                    subscriber.beforeUpdate
195
                ) {
196
                    const executionResult = subscriber.beforeUpdate({
3✔
197
                        connection: this.queryRunner.connection,
198
                        queryRunner: this.queryRunner,
199
                        manager: this.queryRunner.manager,
200
                        entity: entity,
201
                        metadata: metadata,
202
                        databaseEntity: databaseEntity,
203
                        updatedColumns: updatedColumns || [],
3!
204
                        updatedRelations: updatedRelations || [],
3!
205
                    })
206
                    if (executionResult instanceof Promise)
3!
UNCOV
207
                        result.promises.push(executionResult)
×
208
                    result.count++
3✔
209
                }
210
            })
211
        }
212
    }
213

214
    /**
215
     * Broadcasts "BEFORE_REMOVE" event.
216
     * Before remove event is executed before entity is being removed from the database.
217
     * All subscribers and entity listeners who listened to this event will be executed at this point.
218
     * Subscribers and entity listeners can return promises, it will wait until they are resolved.
219
     *
220
     * Note: this method has a performance-optimized code organization, do not change code structure.
221
     */
222
    broadcastBeforeRemoveEvent(
223
        result: BroadcasterResult,
224
        metadata: EntityMetadata,
225
        entity?: ObjectLiteral,
226
        databaseEntity?: ObjectLiteral,
227
        identifier?: ObjectLiteral,
228
    ): void {
229
        if (entity && metadata.beforeRemoveListeners.length) {
4!
230
            metadata.beforeRemoveListeners.forEach((listener) => {
×
231
                if (listener.isAllowed(entity)) {
×
232
                    const executionResult = listener.execute(entity)
×
233
                    if (executionResult instanceof Promise)
×
234
                        result.promises.push(executionResult)
×
235
                    result.count++
×
236
                }
237
            })
238
        }
239

240
        if (this.queryRunner.connection.subscribers.length) {
4!
241
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
×
242
                if (
×
243
                    this.isAllowedSubscriber(subscriber, metadata.target) &&
×
244
                    subscriber.beforeRemove
245
                ) {
246
                    const executionResult = subscriber.beforeRemove({
×
247
                        connection: this.queryRunner.connection,
248
                        queryRunner: this.queryRunner,
249
                        manager: this.queryRunner.manager,
250
                        entity: entity,
251
                        metadata: metadata,
252
                        databaseEntity: databaseEntity,
253
                        entityId: metadata.getEntityIdMixedMap(
254
                            databaseEntity ?? identifier,
×
255
                        ),
256
                    })
257
                    if (executionResult instanceof Promise)
×
258
                        result.promises.push(executionResult)
×
259
                    result.count++
×
260
                }
261
            })
262
        }
263
    }
264

265
    /**
266
     * Broadcasts "BEFORE_SOFT_REMOVE" event.
267
     * Before soft remove event is executed before entity is being soft removed from the database.
268
     * All subscribers and entity listeners who listened to this event will be executed at this point.
269
     * Subscribers and entity listeners can return promises, it will wait until they are resolved.
270
     *
271
     * Note: this method has a performance-optimized code organization, do not change code structure.
272
     */
273
    broadcastBeforeSoftRemoveEvent(
274
        result: BroadcasterResult,
275
        metadata: EntityMetadata,
276
        entity?: ObjectLiteral,
277
        databaseEntity?: ObjectLiteral,
278
        identifier?: ObjectLiteral,
279
    ): void {
280
        if (entity && metadata.beforeSoftRemoveListeners.length) {
3!
UNCOV
281
            metadata.beforeSoftRemoveListeners.forEach((listener) => {
×
UNCOV
282
                if (listener.isAllowed(entity)) {
×
UNCOV
283
                    const executionResult = listener.execute(entity)
×
UNCOV
284
                    if (executionResult instanceof Promise)
×
285
                        result.promises.push(executionResult)
×
UNCOV
286
                    result.count++
×
287
                }
288
            })
289
        }
290

291
        if (this.queryRunner.connection.subscribers.length) {
3!
UNCOV
292
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
×
UNCOV
293
                if (
×
294
                    this.isAllowedSubscriber(subscriber, metadata.target) &&
×
295
                    subscriber.beforeSoftRemove
296
                ) {
UNCOV
297
                    const executionResult = subscriber.beforeSoftRemove({
×
298
                        connection: this.queryRunner.connection,
299
                        queryRunner: this.queryRunner,
300
                        manager: this.queryRunner.manager,
301
                        entity: entity,
302
                        metadata: metadata,
303
                        databaseEntity: databaseEntity,
304
                        entityId: metadata.getEntityIdMixedMap(
305
                            databaseEntity ?? identifier,
×
306
                        ),
307
                    })
UNCOV
308
                    if (executionResult instanceof Promise)
×
309
                        result.promises.push(executionResult)
×
UNCOV
310
                    result.count++
×
311
                }
312
            })
313
        }
314
    }
315

316
    /**
317
     * Broadcasts "BEFORE_RECOVER" event.
318
     * Before recover event is executed before entity is being recovered in the database.
319
     * All subscribers and entity listeners who listened to this event will be executed at this point.
320
     * Subscribers and entity listeners can return promises, it will wait until they are resolved.
321
     *
322
     * Note: this method has a performance-optimized code organization, do not change code structure.
323
     */
324
    broadcastBeforeRecoverEvent(
325
        result: BroadcasterResult,
326
        metadata: EntityMetadata,
327
        entity?: ObjectLiteral,
328
        databaseEntity?: ObjectLiteral,
329
        identifier?: ObjectLiteral,
330
    ): void {
UNCOV
331
        if (entity && metadata.beforeRecoverListeners.length) {
×
UNCOV
332
            metadata.beforeRecoverListeners.forEach((listener) => {
×
UNCOV
333
                if (listener.isAllowed(entity)) {
×
UNCOV
334
                    const executionResult = listener.execute(entity)
×
UNCOV
335
                    if (executionResult instanceof Promise)
×
336
                        result.promises.push(executionResult)
×
UNCOV
337
                    result.count++
×
338
                }
339
            })
340
        }
341

UNCOV
342
        if (this.queryRunner.connection.subscribers.length) {
×
UNCOV
343
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
×
UNCOV
344
                if (
×
345
                    this.isAllowedSubscriber(subscriber, metadata.target) &&
×
346
                    subscriber.beforeRecover
347
                ) {
UNCOV
348
                    const executionResult = subscriber.beforeRecover({
×
349
                        connection: this.queryRunner.connection,
350
                        queryRunner: this.queryRunner,
351
                        manager: this.queryRunner.manager,
352
                        entity: entity,
353
                        metadata: metadata,
354
                        databaseEntity: databaseEntity,
355
                        entityId: metadata.getEntityIdMixedMap(
356
                            databaseEntity ?? identifier,
×
357
                        ),
358
                    })
UNCOV
359
                    if (executionResult instanceof Promise)
×
360
                        result.promises.push(executionResult)
×
UNCOV
361
                    result.count++
×
362
                }
363
            })
364
        }
365
    }
366

367
    /**
368
     * Broadcasts "AFTER_INSERT" event.
369
     * After insert event is executed after entity is being persisted to the database for the first time.
370
     * All subscribers and entity listeners who listened to this event will be executed at this point.
371
     * Subscribers and entity listeners can return promises, it will wait until they are resolved.
372
     *
373
     * Note: this method has a performance-optimized code organization, do not change code structure.
374
     */
375
    broadcastAfterInsertEvent(
376
        result: BroadcasterResult,
377
        metadata: EntityMetadata,
378
        entity?: ObjectLiteral,
379
        identifier?: ObjectLiteral,
380
    ): void {
381
        if (entity && metadata.afterInsertListeners.length) {
252!
382
            metadata.afterInsertListeners.forEach((listener) => {
×
383
                if (listener.isAllowed(entity)) {
×
384
                    const executionResult = listener.execute(entity)
×
385
                    if (executionResult instanceof Promise)
×
386
                        result.promises.push(executionResult)
×
387
                    result.count++
×
388
                }
389
            })
390
        }
391

392
        if (this.queryRunner.connection.subscribers.length) {
252✔
393
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
2✔
394
                if (
2!
395
                    this.isAllowedSubscriber(subscriber, metadata.target) &&
4✔
396
                    subscriber.afterInsert
397
                ) {
UNCOV
398
                    const executionResult = subscriber.afterInsert({
×
399
                        connection: this.queryRunner.connection,
400
                        queryRunner: this.queryRunner,
401
                        manager: this.queryRunner.manager,
402
                        entity: entity,
403
                        metadata: metadata,
404
                        entityId: metadata.getEntityIdMixedMap(identifier),
405
                    })
UNCOV
406
                    if (executionResult instanceof Promise)
×
407
                        result.promises.push(executionResult)
×
UNCOV
408
                    result.count++
×
409
                }
410
            })
411
        }
412
    }
413

414
    /**
415
     * Broadcasts "BEFORE_QUERY" event.
416
     */
417
    broadcastBeforeQueryEvent(
418
        result: BroadcasterResult,
419
        query: string,
420
        parameters: undefined | any[],
421
    ): void {
UNCOV
422
        if (this.queryRunner.connection.subscribers.length) {
×
UNCOV
423
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
×
UNCOV
424
                if (subscriber.beforeQuery) {
×
UNCOV
425
                    const executionResult = subscriber.beforeQuery({
×
426
                        connection: this.queryRunner.connection,
427
                        queryRunner: this.queryRunner,
428
                        manager: this.queryRunner.manager,
429
                        query: query,
430
                        parameters: parameters,
431
                    })
UNCOV
432
                    if (executionResult instanceof Promise)
×
UNCOV
433
                        result.promises.push(executionResult)
×
UNCOV
434
                    result.count++
×
435
                }
436
            })
437
        }
438
    }
439

440
    /**
441
     * Broadcasts "AFTER_QUERY" event.
442
     */
443
    broadcastAfterQueryEvent(
444
        result: BroadcasterResult,
445
        query: string,
446
        parameters: undefined | any[],
447
        success: boolean,
448
        executionTime: undefined | number,
449
        rawResults: undefined | any,
450
        error: undefined | any,
451
    ): void {
UNCOV
452
        if (this.queryRunner.connection.subscribers.length) {
×
UNCOV
453
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
×
UNCOV
454
                if (subscriber.afterQuery) {
×
UNCOV
455
                    const executionResult = subscriber.afterQuery({
×
456
                        connection: this.queryRunner.connection,
457
                        queryRunner: this.queryRunner,
458
                        manager: this.queryRunner.manager,
459
                        query: query,
460
                        parameters: parameters,
461
                        success: success,
462
                        executionTime: executionTime,
463
                        rawResults: rawResults,
464
                        error: error,
465
                    })
UNCOV
466
                    if (executionResult instanceof Promise)
×
467
                        result.promises.push(executionResult)
×
UNCOV
468
                    result.count++
×
469
                }
470
            })
471
        }
472
    }
473

474
    /**
475
     * Broadcasts "BEFORE_TRANSACTION_START" event.
476
     */
477
    broadcastBeforeTransactionStartEvent(result: BroadcasterResult): void {
UNCOV
478
        if (this.queryRunner.connection.subscribers.length) {
×
UNCOV
479
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
×
UNCOV
480
                if (subscriber.beforeTransactionStart) {
×
UNCOV
481
                    const executionResult = subscriber.beforeTransactionStart({
×
482
                        connection: this.queryRunner.connection,
483
                        queryRunner: this.queryRunner,
484
                        manager: this.queryRunner.manager,
485
                    })
UNCOV
486
                    if (executionResult instanceof Promise)
×
487
                        result.promises.push(executionResult)
×
UNCOV
488
                    result.count++
×
489
                }
490
            })
491
        }
492
    }
493

494
    /**
495
     * Broadcasts "AFTER_TRANSACTION_START" event.
496
     */
497
    broadcastAfterTransactionStartEvent(result: BroadcasterResult): void {
UNCOV
498
        if (this.queryRunner.connection.subscribers.length) {
×
UNCOV
499
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
×
UNCOV
500
                if (subscriber.afterTransactionStart) {
×
UNCOV
501
                    const executionResult = subscriber.afterTransactionStart({
×
502
                        connection: this.queryRunner.connection,
503
                        queryRunner: this.queryRunner,
504
                        manager: this.queryRunner.manager,
505
                    })
UNCOV
506
                    if (executionResult instanceof Promise)
×
507
                        result.promises.push(executionResult)
×
UNCOV
508
                    result.count++
×
509
                }
510
            })
511
        }
512
    }
513

514
    /**
515
     * Broadcasts "BEFORE_TRANSACTION_COMMIT" event.
516
     */
517
    broadcastBeforeTransactionCommitEvent(result: BroadcasterResult): void {
UNCOV
518
        if (this.queryRunner.connection.subscribers.length) {
×
UNCOV
519
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
×
UNCOV
520
                if (subscriber.beforeTransactionCommit) {
×
UNCOV
521
                    const executionResult = subscriber.beforeTransactionCommit({
×
522
                        connection: this.queryRunner.connection,
523
                        queryRunner: this.queryRunner,
524
                        manager: this.queryRunner.manager,
525
                    })
UNCOV
526
                    if (executionResult instanceof Promise)
×
527
                        result.promises.push(executionResult)
×
UNCOV
528
                    result.count++
×
529
                }
530
            })
531
        }
532
    }
533

534
    /**
535
     * Broadcasts "AFTER_TRANSACTION_COMMIT" event.
536
     */
537
    broadcastAfterTransactionCommitEvent(result: BroadcasterResult): void {
UNCOV
538
        if (this.queryRunner.connection.subscribers.length) {
×
UNCOV
539
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
×
UNCOV
540
                if (subscriber.afterTransactionCommit) {
×
UNCOV
541
                    const executionResult = subscriber.afterTransactionCommit({
×
542
                        connection: this.queryRunner.connection,
543
                        queryRunner: this.queryRunner,
544
                        manager: this.queryRunner.manager,
545
                    })
UNCOV
546
                    if (executionResult instanceof Promise)
×
547
                        result.promises.push(executionResult)
×
UNCOV
548
                    result.count++
×
549
                }
550
            })
551
        }
552
    }
553

554
    /**
555
     * Broadcasts "BEFORE_TRANSACTION_ROLLBACK" event.
556
     */
557
    broadcastBeforeTransactionRollbackEvent(result: BroadcasterResult): void {
UNCOV
558
        if (this.queryRunner.connection.subscribers.length) {
×
UNCOV
559
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
×
UNCOV
560
                if (subscriber.beforeTransactionRollback) {
×
561
                    const executionResult =
UNCOV
562
                        subscriber.beforeTransactionRollback({
×
563
                            connection: this.queryRunner.connection,
564
                            queryRunner: this.queryRunner,
565
                            manager: this.queryRunner.manager,
566
                        })
UNCOV
567
                    if (executionResult instanceof Promise)
×
568
                        result.promises.push(executionResult)
×
UNCOV
569
                    result.count++
×
570
                }
571
            })
572
        }
573
    }
574

575
    /**
576
     * Broadcasts "AFTER_TRANSACTION_ROLLBACK" event.
577
     */
578
    broadcastAfterTransactionRollbackEvent(result: BroadcasterResult): void {
UNCOV
579
        if (this.queryRunner.connection.subscribers.length) {
×
UNCOV
580
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
×
UNCOV
581
                if (subscriber.afterTransactionRollback) {
×
UNCOV
582
                    const executionResult = subscriber.afterTransactionRollback(
×
583
                        {
584
                            connection: this.queryRunner.connection,
585
                            queryRunner: this.queryRunner,
586
                            manager: this.queryRunner.manager,
587
                        },
588
                    )
UNCOV
589
                    if (executionResult instanceof Promise)
×
590
                        result.promises.push(executionResult)
×
UNCOV
591
                    result.count++
×
592
                }
593
            })
594
        }
595
    }
596

597
    /**
598
     * Broadcasts "AFTER_UPDATE" event.
599
     * After update event is executed after entity is being updated in the database.
600
     * All subscribers and entity listeners who listened to this event will be executed at this point.
601
     * Subscribers and entity listeners can return promises, it will wait until they are resolved.
602
     *
603
     * Note: this method has a performance-optimized code organization, do not change code structure.
604
     */
605
    broadcastAfterUpdateEvent(
606
        result: BroadcasterResult,
607
        metadata: EntityMetadata,
608
        entity?: ObjectLiteral,
609
        databaseEntity?: ObjectLiteral,
610
        updatedColumns?: ColumnMetadata[],
611
        updatedRelations?: RelationMetadata[],
612
    ): void {
613
        if (entity && metadata.afterUpdateListeners.length) {
12!
UNCOV
614
            metadata.afterUpdateListeners.forEach((listener) => {
×
UNCOV
615
                if (listener.isAllowed(entity)) {
×
UNCOV
616
                    const executionResult = listener.execute(entity)
×
UNCOV
617
                    if (executionResult instanceof Promise)
×
618
                        result.promises.push(executionResult)
×
UNCOV
619
                    result.count++
×
620
                }
621
            })
622
        }
623

624
        if (this.queryRunner.connection.subscribers.length) {
12✔
625
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
3✔
626
                if (
3!
627
                    this.isAllowedSubscriber(subscriber, metadata.target) &&
6✔
628
                    subscriber.afterUpdate
629
                ) {
UNCOV
630
                    const executionResult = subscriber.afterUpdate({
×
631
                        connection: this.queryRunner.connection,
632
                        queryRunner: this.queryRunner,
633
                        manager: this.queryRunner.manager,
634
                        entity: entity,
635
                        metadata: metadata,
636
                        databaseEntity: databaseEntity,
637
                        updatedColumns: updatedColumns || [],
×
638
                        updatedRelations: updatedRelations || [],
×
639
                    })
UNCOV
640
                    if (executionResult instanceof Promise)
×
641
                        result.promises.push(executionResult)
×
UNCOV
642
                    result.count++
×
643
                }
644
            })
645
        }
646
    }
647

648
    /**
649
     * Broadcasts "AFTER_REMOVE" event.
650
     * After remove event is executed after entity is being removed from the database.
651
     * All subscribers and entity listeners who listened to this event will be executed at this point.
652
     * Subscribers and entity listeners can return promises, it will wait until they are resolved.
653
     *
654
     * Note: this method has a performance-optimized code organization, do not change code structure.
655
     */
656
    broadcastAfterRemoveEvent(
657
        result: BroadcasterResult,
658
        metadata: EntityMetadata,
659
        entity?: ObjectLiteral,
660
        databaseEntity?: ObjectLiteral,
661
        identifier?: ObjectLiteral,
662
    ): void {
663
        if (entity && metadata.afterRemoveListeners.length) {
4!
UNCOV
664
            metadata.afterRemoveListeners.forEach((listener) => {
×
UNCOV
665
                if (listener.isAllowed(entity)) {
×
UNCOV
666
                    const executionResult = listener.execute(entity)
×
UNCOV
667
                    if (executionResult instanceof Promise)
×
668
                        result.promises.push(executionResult)
×
UNCOV
669
                    result.count++
×
670
                }
671
            })
672
        }
673

674
        if (this.queryRunner.connection.subscribers.length) {
4!
675
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
×
676
                if (
×
677
                    this.isAllowedSubscriber(subscriber, metadata.target) &&
×
678
                    subscriber.afterRemove
679
                ) {
680
                    const executionResult = subscriber.afterRemove({
×
681
                        connection: this.queryRunner.connection,
682
                        queryRunner: this.queryRunner,
683
                        manager: this.queryRunner.manager,
684
                        entity: entity,
685
                        metadata: metadata,
686
                        databaseEntity: databaseEntity,
687
                        entityId: metadata.getEntityIdMixedMap(
688
                            databaseEntity ?? identifier,
×
689
                        ),
690
                    })
691
                    if (executionResult instanceof Promise)
×
692
                        result.promises.push(executionResult)
×
693
                    result.count++
×
694
                }
695
            })
696
        }
697
    }
698

699
    /**
700
     * Broadcasts "AFTER_SOFT_REMOVE" event.
701
     * After soft remove event is executed after entity is being soft removed from the database.
702
     * All subscribers and entity listeners who listened to this event will be executed at this point.
703
     * Subscribers and entity listeners can return promises, it will wait until they are resolved.
704
     *
705
     * Note: this method has a performance-optimized code organization, do not change code structure.
706
     */
707
    broadcastAfterSoftRemoveEvent(
708
        result: BroadcasterResult,
709
        metadata: EntityMetadata,
710
        entity?: ObjectLiteral,
711
        databaseEntity?: ObjectLiteral,
712
        identifier?: ObjectLiteral,
713
    ): void {
714
        if (entity && metadata.afterSoftRemoveListeners.length) {
3!
UNCOV
715
            metadata.afterSoftRemoveListeners.forEach((listener) => {
×
UNCOV
716
                if (listener.isAllowed(entity)) {
×
UNCOV
717
                    const executionResult = listener.execute(entity)
×
UNCOV
718
                    if (executionResult instanceof Promise)
×
719
                        result.promises.push(executionResult)
×
UNCOV
720
                    result.count++
×
721
                }
722
            })
723
        }
724

725
        if (this.queryRunner.connection.subscribers.length) {
3!
UNCOV
726
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
×
UNCOV
727
                if (
×
728
                    this.isAllowedSubscriber(subscriber, metadata.target) &&
×
729
                    subscriber.afterSoftRemove
730
                ) {
UNCOV
731
                    const executionResult = subscriber.afterSoftRemove({
×
732
                        connection: this.queryRunner.connection,
733
                        queryRunner: this.queryRunner,
734
                        manager: this.queryRunner.manager,
735
                        entity: entity,
736
                        metadata: metadata,
737
                        databaseEntity: databaseEntity,
738
                        entityId: metadata.getEntityIdMixedMap(
739
                            databaseEntity ?? identifier,
×
740
                        ),
741
                    })
UNCOV
742
                    if (executionResult instanceof Promise)
×
743
                        result.promises.push(executionResult)
×
UNCOV
744
                    result.count++
×
745
                }
746
            })
747
        }
748
    }
749

750
    /**
751
     * Broadcasts "AFTER_RECOVER" event.
752
     * After recover event is executed after entity is being recovered in the database.
753
     * All subscribers and entity listeners who listened to this event will be executed at this point.
754
     * Subscribers and entity listeners can return promises, it will wait until they are resolved.
755
     *
756
     * Note: this method has a performance-optimized code organization, do not change code structure.
757
     */
758
    broadcastAfterRecoverEvent(
759
        result: BroadcasterResult,
760
        metadata: EntityMetadata,
761
        entity?: ObjectLiteral,
762
        databaseEntity?: ObjectLiteral,
763
        identifier?: ObjectLiteral,
764
    ): void {
UNCOV
765
        if (entity && metadata.afterRecoverListeners.length) {
×
UNCOV
766
            metadata.afterRecoverListeners.forEach((listener) => {
×
UNCOV
767
                if (listener.isAllowed(entity)) {
×
UNCOV
768
                    const executionResult = listener.execute(entity)
×
UNCOV
769
                    if (executionResult instanceof Promise)
×
770
                        result.promises.push(executionResult)
×
UNCOV
771
                    result.count++
×
772
                }
773
            })
774
        }
775

UNCOV
776
        if (this.queryRunner.connection.subscribers.length) {
×
UNCOV
777
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
×
UNCOV
778
                if (
×
779
                    this.isAllowedSubscriber(subscriber, metadata.target) &&
×
780
                    subscriber.afterRecover
781
                ) {
UNCOV
782
                    const executionResult = subscriber.afterRecover({
×
783
                        connection: this.queryRunner.connection,
784
                        queryRunner: this.queryRunner,
785
                        manager: this.queryRunner.manager,
786
                        entity: entity,
787
                        metadata: metadata,
788
                        databaseEntity: databaseEntity,
789
                        entityId: metadata.getEntityIdMixedMap(
790
                            databaseEntity ?? identifier,
×
791
                        ),
792
                    })
UNCOV
793
                    if (executionResult instanceof Promise)
×
794
                        result.promises.push(executionResult)
×
UNCOV
795
                    result.count++
×
796
                }
797
            })
798
        }
799
    }
800

801
    /**
802
     * @deprecated Use `broadcastLoadForAllEvent`
803
     */
804
    broadcastLoadEventsForAll(
805
        result: BroadcasterResult,
806
        metadata: EntityMetadata,
807
        entities: ObjectLiteral[],
808
    ): void {
809
        return this.broadcastLoadEvent(result, metadata, entities)
×
810
    }
811

812
    /**
813
     * Broadcasts "AFTER_LOAD" event for all given entities, and their sub-entities.
814
     * After load event is executed after entity has been loaded from the database.
815
     * All subscribers and entity listeners who listened to this event will be executed at this point.
816
     * Subscribers and entity listeners can return promises, it will wait until they are resolved.
817
     *
818
     * Note: this method has a performance-optimized code organization, do not change code structure.
819
     */
820
    broadcastLoadEvent(
821
        result: BroadcasterResult,
822
        metadata: EntityMetadata,
823
        entities: ObjectLiteral[],
824
    ): void {
825
        // Calculate which subscribers are fitting for the given entity type
826
        const fittingSubscribers =
827
            this.queryRunner.connection.subscribers.filter(
176✔
828
                (subscriber) =>
829
                    this.isAllowedSubscriber(subscriber, metadata.target) &&
12✔
830
                    subscriber.afterLoad,
831
            )
832

833
        if (
176✔
834
            metadata.relations.length ||
508✔
835
            metadata.afterLoadListeners.length ||
836
            fittingSubscribers.length
837
        ) {
838
            // todo: check why need this?
839
            const nonPromiseEntities = entities.filter(
32✔
840
                (entity) => !(entity instanceof Promise),
32✔
841
            )
842

843
            // collect load events for all children entities that were loaded with the main entity
844
            if (metadata.relations.length) {
32!
UNCOV
845
                metadata.relations.forEach((relation) => {
×
UNCOV
846
                    nonPromiseEntities.forEach((entity) => {
×
847
                        // in lazy relations we cannot simply access to entity property because it will cause a getter and a database query
UNCOV
848
                        if (
×
849
                            relation.isLazy &&
×
850
                            !entity.hasOwnProperty(relation.propertyName)
851
                        )
UNCOV
852
                            return
×
853

UNCOV
854
                        const value = relation.getEntityValue(entity)
×
UNCOV
855
                        if (ObjectUtils.isObject(value))
×
UNCOV
856
                            this.broadcastLoadEvent(
×
857
                                result,
858
                                relation.inverseEntityMetadata,
859
                                Array.isArray(value) ? value : [value],
×
860
                            )
861
                    })
862
                })
863
            }
864

865
            if (metadata.afterLoadListeners.length) {
32✔
866
                metadata.afterLoadListeners.forEach((listener) => {
20✔
867
                    nonPromiseEntities.forEach((entity) => {
20✔
868
                        if (listener.isAllowed(entity)) {
20✔
869
                            const executionResult = listener.execute(entity)
20✔
870
                            if (executionResult instanceof Promise)
20✔
871
                                result.promises.push(executionResult)
12✔
872
                            result.count++
20✔
873
                        }
874
                    })
875
                })
876
            }
877

878
            fittingSubscribers.forEach((subscriber) => {
32✔
879
                nonPromiseEntities.forEach((entity) => {
12✔
880
                    const executionResult = subscriber.afterLoad!(entity, {
12✔
881
                        entity,
882
                        metadata,
883
                        connection: this.queryRunner.connection,
884
                        queryRunner: this.queryRunner,
885
                        manager: this.queryRunner.manager,
886
                    })
887
                    if (executionResult instanceof Promise)
12!
UNCOV
888
                        result.promises.push(executionResult)
×
889
                    result.count++
12✔
890
                })
891
            })
892
        }
893
    }
894

895
    // -------------------------------------------------------------------------
896
    // Protected Methods
897
    // -------------------------------------------------------------------------
898

899
    /**
900
     * Checks if subscriber's methods can be executed by checking if its don't listen to the particular entity,
901
     * or listens our entity.
902
     */
903
    protected isAllowedSubscriber(
904
        subscriber: EntitySubscriberInterface<any>,
905
        target: Function | string,
906
    ): boolean {
907
        return (
22✔
908
            !subscriber.listenTo ||
88!
909
            !subscriber.listenTo() ||
910
            subscriber.listenTo() === Object ||
911
            subscriber.listenTo() === target ||
912
            subscriber.listenTo().isPrototypeOf(target)
913
        )
914
    }
915
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc