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

typeorm / typeorm / 14796576772

02 May 2025 01:52PM UTC coverage: 45.367% (-30.9%) from 76.309%
14796576772

Pull #11434

github

web-flow
Merge ec4ce2d00 into fadad1a74
Pull Request #11434: feat: release PR releases using pkg.pr.new

5216 of 12761 branches covered (40.87%)

Branch coverage included in aggregate %.

11439 of 23951 relevant lines covered (47.76%)

15712.55 hits per line

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

73.82
/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"
4✔
7
import { BroadcasterResult } from "./BroadcasterResult"
4✔
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 {
4✔
92
    // -------------------------------------------------------------------------
93
    // Constructor
94
    // -------------------------------------------------------------------------
95

96
    constructor(private queryRunner: QueryRunner) {}
12,472✔
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()
348,058✔
107

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

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

114
        await result.wait()
348,058✔
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) {
48,356✔
131
            metadata.beforeInsertListeners.forEach((listener) => {
16✔
132
                if (listener.isAllowed(entity)) {
20✔
133
                    const executionResult = listener.execute(entity)
20✔
134
                    if (executionResult instanceof Promise)
20!
135
                        result.promises.push(executionResult)
×
136
                    result.count++
20✔
137
                }
138
            })
139
        }
140

141
        if (this.queryRunner.connection.subscribers.length) {
48,356✔
142
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
68✔
143
                if (
76✔
144
                    this.isAllowedSubscriber(subscriber, metadata.target) &&
148✔
145
                    subscriber.beforeInsert
146
                ) {
147
                    const executionResult = subscriber.beforeInsert({
24✔
148
                        connection: this.queryRunner.connection,
149
                        queryRunner: this.queryRunner,
150
                        manager: this.queryRunner.manager,
151
                        entity: entity,
152
                        metadata: metadata,
153
                    })
154
                    if (executionResult instanceof Promise)
24✔
155
                        result.promises.push(executionResult)
8✔
156
                    result.count++
24✔
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) {
2,298✔
180
            metadata.beforeUpdateListeners.forEach((listener) => {
12✔
181
                if (listener.isAllowed(entity)) {
12✔
182
                    const executionResult = listener.execute(entity)
12✔
183
                    if (executionResult instanceof Promise)
12!
184
                        result.promises.push(executionResult)
×
185
                    result.count++
12✔
186
                }
187
            })
188
        }
189

190
        if (this.queryRunner.connection.subscribers.length) {
2,298✔
191
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
48✔
192
                if (
48✔
193
                    this.isAllowedSubscriber(subscriber, metadata.target) &&
96✔
194
                    subscriber.beforeUpdate
195
                ) {
196
                    const executionResult = subscriber.beforeUpdate({
32✔
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 || [],
36✔
204
                        updatedRelations: updatedRelations || [],
36✔
205
                    })
206
                    if (executionResult instanceof Promise)
32✔
207
                        result.promises.push(executionResult)
4✔
208
                    result.count++
32✔
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) {
419!
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) {
419!
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) {
100✔
281
            metadata.beforeSoftRemoveListeners.forEach((listener) => {
8✔
282
                if (listener.isAllowed(entity)) {
8✔
283
                    const executionResult = listener.execute(entity)
8✔
284
                    if (executionResult instanceof Promise)
8!
285
                        result.promises.push(executionResult)
×
286
                    result.count++
8✔
287
                }
288
            })
289
        }
290

291
        if (this.queryRunner.connection.subscribers.length) {
100✔
292
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
8✔
293
                if (
8✔
294
                    this.isAllowedSubscriber(subscriber, metadata.target) &&
16✔
295
                    subscriber.beforeSoftRemove
296
                ) {
297
                    const executionResult = subscriber.beforeSoftRemove({
4✔
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,
4!
306
                        ),
307
                    })
308
                    if (executionResult instanceof Promise)
4!
309
                        result.promises.push(executionResult)
×
310
                    result.count++
4✔
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 {
331
        if (entity && metadata.beforeRecoverListeners.length) {
44✔
332
            metadata.beforeRecoverListeners.forEach((listener) => {
4✔
333
                if (listener.isAllowed(entity)) {
4✔
334
                    const executionResult = listener.execute(entity)
4✔
335
                    if (executionResult instanceof Promise)
4!
336
                        result.promises.push(executionResult)
×
337
                    result.count++
4✔
338
                }
339
            })
340
        }
341

342
        if (this.queryRunner.connection.subscribers.length) {
44✔
343
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
8✔
344
                if (
8✔
345
                    this.isAllowedSubscriber(subscriber, metadata.target) &&
16✔
346
                    subscriber.beforeRecover
347
                ) {
348
                    const executionResult = subscriber.beforeRecover({
4✔
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,
4!
357
                        ),
358
                    })
359
                    if (executionResult instanceof Promise)
4!
360
                        result.promises.push(executionResult)
×
361
                    result.count++
4✔
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) {
48,332!
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) {
48,332✔
393
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
68✔
394
                if (
76✔
395
                    this.isAllowedSubscriber(subscriber, metadata.target) &&
148✔
396
                    subscriber.afterInsert
397
                ) {
398
                    const executionResult = subscriber.afterInsert({
8✔
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
                    })
406
                    if (executionResult instanceof Promise)
8!
407
                        result.promises.push(executionResult)
×
408
                    result.count++
8✔
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 {
422
        if (this.queryRunner.connection.subscribers.length) {
263,783✔
423
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
1,774✔
424
                if (subscriber.beforeQuery) {
1,911✔
425
                    const executionResult = subscriber.beforeQuery({
61✔
426
                        connection: this.queryRunner.connection,
427
                        queryRunner: this.queryRunner,
428
                        manager: this.queryRunner.manager,
429
                        query: query,
430
                        parameters: parameters,
431
                    })
432
                    if (executionResult instanceof Promise)
61!
433
                        result.promises.push(executionResult)
×
434
                    result.count++
61✔
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 {
452
        if (this.queryRunner.connection.subscribers.length) {
263,774✔
453
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
1,774✔
454
                if (subscriber.afterQuery) {
1,911✔
455
                    const executionResult = subscriber.afterQuery({
61✔
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
                    })
466
                    if (executionResult instanceof Promise)
61!
467
                        result.promises.push(executionResult)
×
468
                    result.count++
61✔
469
                }
470
            })
471
        }
472
    }
473

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

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

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

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

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

575
    /**
576
     * Broadcasts "AFTER_TRANSACTION_ROLLBACK" event.
577
     */
578
    broadcastAfterTransactionRollbackEvent(result: BroadcasterResult): void {
579
        if (this.queryRunner.connection.subscribers.length) {
102✔
580
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
4✔
581
                if (subscriber.afterTransactionRollback) {
4✔
582
                    const executionResult = subscriber.afterTransactionRollback(
4✔
583
                        {
584
                            connection: this.queryRunner.connection,
585
                            queryRunner: this.queryRunner,
586
                            manager: this.queryRunner.manager,
587
                        },
588
                    )
589
                    if (executionResult instanceof Promise)
4!
590
                        result.promises.push(executionResult)
×
591
                    result.count++
4✔
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) {
2,278✔
614
            metadata.afterUpdateListeners.forEach((listener) => {
4✔
615
                if (listener.isAllowed(entity)) {
4✔
616
                    const executionResult = listener.execute(entity)
4✔
617
                    if (executionResult instanceof Promise)
4!
618
                        result.promises.push(executionResult)
×
619
                    result.count++
4✔
620
                }
621
            })
622
        }
623

624
        if (this.queryRunner.connection.subscribers.length) {
2,278✔
625
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
52✔
626
                if (
52✔
627
                    this.isAllowedSubscriber(subscriber, metadata.target) &&
104✔
628
                    subscriber.afterUpdate
629
                ) {
630
                    const executionResult = subscriber.afterUpdate({
24✔
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 || [],
28✔
638
                        updatedRelations: updatedRelations || [],
28✔
639
                    })
640
                    if (executionResult instanceof Promise)
24!
641
                        result.promises.push(executionResult)
×
642
                    result.count++
24✔
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) {
415✔
664
            metadata.afterRemoveListeners.forEach((listener) => {
4✔
665
                if (listener.isAllowed(entity)) {
4✔
666
                    const executionResult = listener.execute(entity)
4✔
667
                    if (executionResult instanceof Promise)
4!
668
                        result.promises.push(executionResult)
×
669
                    result.count++
4✔
670
                }
671
            })
672
        }
673

674
        if (this.queryRunner.connection.subscribers.length) {
415!
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) {
84✔
715
            metadata.afterSoftRemoveListeners.forEach((listener) => {
8✔
716
                if (listener.isAllowed(entity)) {
8✔
717
                    const executionResult = listener.execute(entity)
8✔
718
                    if (executionResult instanceof Promise)
8!
719
                        result.promises.push(executionResult)
×
720
                    result.count++
8✔
721
                }
722
            })
723
        }
724

725
        if (this.queryRunner.connection.subscribers.length) {
84✔
726
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
8✔
727
                if (
8✔
728
                    this.isAllowedSubscriber(subscriber, metadata.target) &&
16✔
729
                    subscriber.afterSoftRemove
730
                ) {
731
                    const executionResult = subscriber.afterSoftRemove({
8✔
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,
8!
740
                        ),
741
                    })
742
                    if (executionResult instanceof Promise)
8!
743
                        result.promises.push(executionResult)
×
744
                    result.count++
8✔
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 {
765
        if (entity && metadata.afterRecoverListeners.length) {
28✔
766
            metadata.afterRecoverListeners.forEach((listener) => {
4✔
767
                if (listener.isAllowed(entity)) {
4✔
768
                    const executionResult = listener.execute(entity)
4✔
769
                    if (executionResult instanceof Promise)
4!
770
                        result.promises.push(executionResult)
×
771
                    result.count++
4✔
772
                }
773
            })
774
        }
775

776
        if (this.queryRunner.connection.subscribers.length) {
28✔
777
            this.queryRunner.connection.subscribers.forEach((subscriber) => {
8✔
778
                if (
8✔
779
                    this.isAllowedSubscriber(subscriber, metadata.target) &&
16✔
780
                    subscriber.afterRecover
781
                ) {
782
                    const executionResult = subscriber.afterRecover({
8✔
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,
8!
791
                        ),
792
                    })
793
                    if (executionResult instanceof Promise)
8!
794
                        result.promises.push(executionResult)
×
795
                    result.count++
8✔
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(
13,962✔
828
                (subscriber) =>
829
                    this.isAllowedSubscriber(subscriber, metadata.target) &&
141✔
830
                    subscriber.afterLoad,
831
            )
832

833
        if (
13,962✔
834
            metadata.relations.length ||
23,770✔
835
            metadata.afterLoadListeners.length ||
836
            fittingSubscribers.length
837
        ) {
838
            // todo: check why need this?
839
            const nonPromiseEntities = entities.filter(
9,062✔
840
                (entity) => !(entity instanceof Promise),
15,200✔
841
            )
842

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

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

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

878
            fittingSubscribers.forEach((subscriber) => {
9,062✔
879
                nonPromiseEntities.forEach((entity) => {
16✔
880
                    const executionResult = subscriber.afterLoad!(entity, {
32✔
881
                        entity,
882
                        metadata,
883
                        connection: this.queryRunner.connection,
884
                        queryRunner: this.queryRunner,
885
                        manager: this.queryRunner.manager,
886
                    })
887
                    if (executionResult instanceof Promise)
32✔
888
                        result.promises.push(executionResult)
16✔
889
                    result.count++
32✔
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 (
425✔
908
            !subscriber.listenTo ||
1,552✔
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