@flusys/nestjs-shared 7.0.0 → 7.0.2

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.
Files changed (40) hide show
  1. package/README.md +132 -84
  2. package/cjs/classes/api-service.class.js +37 -47
  3. package/cjs/classes/api-service.class.spec.js +120 -12
  4. package/cjs/classes/event-bus.registry.js +2 -3
  5. package/cjs/classes/event-bus.registry.spec.js +49 -24
  6. package/cjs/classes/event-transports/kafka-transport.optional.spec.js +1 -2
  7. package/cjs/classes/event-transports/memory-transport.js +3 -4
  8. package/cjs/classes/event-transports/memory-transport.spec.js +1 -2
  9. package/cjs/classes/event-transports/rabbitmq-transport.optional.spec.js +1 -2
  10. package/cjs/classes/hybrid-event-bus.class.js +23 -3
  11. package/cjs/classes/hybrid-event-bus.class.spec.js +31 -2
  12. package/cjs/enums/index.js +0 -1
  13. package/cjs/modules/event-bus/event-bus.integration.spec.js +1 -2
  14. package/cjs/utils/event-pattern.util.js +6 -1
  15. package/cjs/utils/event-pattern.util.spec.js +20 -3
  16. package/classes/api-service.class.d.ts +8 -12
  17. package/classes/domain-event.publisher.d.ts +2 -2
  18. package/classes/event-bus.registry.d.ts +2 -2
  19. package/classes/event-transports/memory-transport.d.ts +1 -1
  20. package/classes/hybrid-event-bus.class.d.ts +2 -0
  21. package/enums/index.d.ts +0 -1
  22. package/fesm/classes/api-service.class.js +37 -47
  23. package/fesm/classes/api-service.class.spec.js +120 -12
  24. package/fesm/classes/event-bus.registry.js +2 -3
  25. package/fesm/classes/event-bus.registry.spec.js +49 -24
  26. package/fesm/classes/event-transports/kafka-transport.optional.spec.js +1 -2
  27. package/fesm/classes/event-transports/memory-transport.js +7 -4
  28. package/fesm/classes/event-transports/memory-transport.spec.js +1 -2
  29. package/fesm/classes/event-transports/rabbitmq-transport.optional.spec.js +1 -2
  30. package/fesm/classes/hybrid-event-bus.class.js +23 -3
  31. package/fesm/classes/hybrid-event-bus.class.spec.js +31 -2
  32. package/fesm/enums/index.js +0 -1
  33. package/fesm/modules/event-bus/event-bus.integration.spec.js +1 -2
  34. package/fesm/utils/event-pattern.util.js +6 -1
  35. package/fesm/utils/event-pattern.util.spec.js +20 -3
  36. package/interfaces/event-bus.interface.d.ts +3 -6
  37. package/package.json +2 -2
  38. package/cjs/enums/event-action.enum.js +0 -18
  39. package/enums/event-action.enum.d.ts +0 -7
  40. package/fesm/enums/event-action.enum.js +0 -8
package/README.md CHANGED
@@ -59,19 +59,21 @@ export class Product extends Identity {
59
59
  }
60
60
  ```
61
61
 
62
- ## 3. Generic CRUD — `createApiController` + `RequestScopedApiService`
62
+ ## 3. Generic CRUD — `createApiController` + `ApiService`
63
63
 
64
64
  ### Service (REQUEST-scoped, DataSource Provider pattern)
65
65
 
66
+ The constructor takes the event entity name, the cache, `UtilsService`, the logger
67
+ name, whether reads are cacheable, the event module name, the DataSource provider
68
+ and the entity. All of them are required, so a service cannot silently skip one.
69
+
66
70
  ```typescript
67
- import { Injectable, Scope, Inject } from '@nestjs/common';
68
- import { EntityTarget, Repository } from 'typeorm';
69
- import { RequestScopedApiService } from '@flusys/nestjs-shared/classes';
70
- import { HybridCache } from '@flusys/nestjs-shared/classes';
71
+ import { Inject, Injectable, Scope } from '@nestjs/common';
72
+ import { ApiService, HybridCache } from '@flusys/nestjs-shared/classes';
71
73
  import { UtilsService } from '@flusys/nestjs-shared/modules';
72
74
 
73
75
  @Injectable({ scope: Scope.REQUEST }) // required
74
- export class ProductService extends RequestScopedApiService<
76
+ export class ProductService extends ApiService<
75
77
  CreateProductDto,
76
78
  UpdateProductDto,
77
79
  IProduct,
@@ -80,23 +82,49 @@ export class ProductService extends RequestScopedApiService<
80
82
  constructor(
81
83
  @Inject('CACHE_INSTANCE') protected override cacheManager: HybridCache,
82
84
  @Inject(UtilsService) protected override utilsService: UtilsService,
83
- @Inject(ProductConfigService) private readonly config: ProductConfigService,
84
- @Inject(ProductDataSourceProvider) private readonly dsp: ProductDataSourceProvider,
85
+ @Inject(ProductDataSourceProvider) dataSourceProvider: ProductDataSourceProvider,
85
86
  ) {
86
- super('product', null as any, cacheManager, utilsService, ProductService.name, true);
87
+ super(
88
+ PRODUCT_EVENT_ENTITIES.PRODUCT,
89
+ cacheManager,
90
+ utilsService,
91
+ ProductService.name,
92
+ true,
93
+ PRODUCT_EVENT_MODULE,
94
+ dataSourceProvider,
95
+ Product,
96
+ );
87
97
  }
98
+ }
99
+ ```
88
100
 
89
- // Required: tell the base class which entity and which datasource to use
90
- protected resolveEntity(): EntityTarget<Product> {
91
- return this.config.isCompanyFeatureEnabled() ? ProductWithCompany : Product;
92
- }
101
+ A service whose table depends on runtime configuration passes the choice in the
102
+ same argument. Read the injected config through the **constructor parameter**, not
103
+ `this` - `this` is not available until `super()` returns:
93
104
 
94
- protected getDataSourceProvider() {
95
- return this.dsp;
105
+ ```typescript
106
+ constructor(
107
+ @Inject('CACHE_INSTANCE') protected override cacheManager: HybridCache,
108
+ @Inject(UtilsService) protected override utilsService: UtilsService,
109
+ @Inject(ProductConfigService) private readonly config: ProductConfigService,
110
+ @Inject(ProductDataSourceProvider) dataSourceProvider: ProductDataSourceProvider,
111
+ ) {
112
+ super(
113
+ PRODUCT_EVENT_ENTITIES.PRODUCT,
114
+ cacheManager,
115
+ utilsService,
116
+ ProductService.name,
117
+ true,
118
+ PRODUCT_EVENT_MODULE,
119
+ dataSourceProvider,
120
+ config.isCompanyFeatureEnabled() ? ProductWithCompany : Product,
121
+ );
96
122
  }
97
- }
98
123
  ```
99
124
 
125
+ Services are `Scope.REQUEST`, so the configuration is read once per request either
126
+ way. The repository itself is still created lazily, on first use.
127
+
100
128
  ### Controller (factory function)
101
129
 
102
130
  ```typescript
@@ -297,7 +325,7 @@ events — no per-service code. Turn it on in the module's own config:
297
325
  StorageModule.forRoot({
298
326
  config: {
299
327
  ...dbConfig,
300
- events: { enabled: true, includePayload: true },
328
+ events: { enabled: true },
301
329
  },
302
330
  });
303
331
  ```
@@ -307,22 +335,45 @@ StorageModule.forRoot({
307
335
  | Option | Type | Default | Effect |
308
336
  | ----------------- | --------------------------- | ------- | ---------------------------------------------------------------------- |
309
337
  | `enabled` | `boolean` | `false` | Master switch for the module. Everything else is ignored when off. |
310
- | `actions` | `(EventAction \| string)[]` | all | Allowlist of action names. Omit to publish every action. |
311
- | `entities` | `string[]` | all | Allowlist of entity names. Omit to publish every entity. |
312
- | `excludeEntities` | `string[]` | `[]` | Denylist, applied before `entities`. |
338
+ | `actions` | `string[]` | all | Allowlist of `entity.action` or bare `action` entries. Omit to publish all. |
313
339
  | `includePayload` | `boolean` | `true` | Attach the saved rows as `payload.data`. Ids and metadata always ride. |
314
340
 
315
- Three rules that decide what actually reaches a consumer:
341
+ Two rules decide what actually reaches a consumer:
316
342
 
317
343
  1. `actions` is **one allowlist covering CRUD and domain actions together**. Listing only
318
344
  `['logged-in']` silences that module's `created`/`updated`/`deleted` too - name
319
345
  everything you want kept, or omit `actions` entirely.
320
- 2. `entities` matches the entity **in the event name**, which for a domain action is not
321
- always the table you expect: `auth.session.logged-in` is filtered by `session`, not
322
- `appuser`.
323
- 3. Entity names are the ones the services register, so some are `snake_case`
324
- (`form_result`, `file_manager`) and some are `camelCase` (`storageConfig`,
325
- `emailConfig`, `translationKey`). The per-module tables below list them verbatim.
346
+ 2. An entry is either `entity.action`, which covers that one entity, or a bare action,
347
+ which covers every entity of the module. So `role.updated` keeps role updates and
348
+ drops `action.updated`, while `updated` keeps every entity's updates. Entries are
349
+ matched as routing patterns, so `role.*` and `*.purged` work as well.
350
+
351
+ There is no separate entity filter: an enabled module publishes under every entity it has
352
+ unless an entry narrows it, so a package gaining one is picked up with no config change.
353
+
354
+ Mixing the two forms narrows one entity without touching the others:
355
+
356
+ ```typescript
357
+ import { IAM_EVENT_ACTIONS } from '@flusys/nestjs-iam/config';
358
+
359
+ IAMModule.forRoot({
360
+ config: {
361
+ ...dbConfig,
362
+ events: {
363
+ enabled: true,
364
+ actions: [
365
+ 'created',
366
+ 'deleted',
367
+ 'role.updated',
368
+ `user_role.${IAM_EVENT_ACTIONS.PERMISSIONS_ASSIGNED}`,
369
+ ],
370
+ },
371
+ },
372
+ });
373
+ ```
374
+
375
+ `iam.role.updated` publishes; `iam.action.updated` does not; both still publish `created`
376
+ and `deleted`.
326
377
 
327
378
  Event names are always `<module>.<entity>.<action>`. CRUD lifecycle actions are
328
379
  `created`, `updated`, `deleted`, `restored`, `purged`. Events are published after the
@@ -336,15 +387,21 @@ there is no row left to attach.
336
387
  Payloads are walked before they leave the process: credential-like fields become
337
388
  `[REDACTED]`, a relation that loops back becomes `[CIRCULAR]`, anything past 8 levels deep
338
389
  becomes `[TRUNCATED]`, and a Buffer becomes `[BINARY]`. Saved TypeORM rows with
339
- bidirectional relations are therefore safe to publish.
390
+ bidirectional relations are therefore safe to publish. The `config` blob on a
391
+ `storageConfig` or `emailConfig` row is redacted whole - S3, Azure, SFTP and SMTP
392
+ credentials live in it - so those rows publish their id, name and provider and nothing
393
+ else. Redaction is a package default, not something an application configures, so it
394
+ cannot be switched off by a config edit.
340
395
 
341
396
  ### Registering each module
342
397
 
343
398
  Every feature module takes the same `events` block in its own config, and every one of
344
399
  them also has a `forRootAsync` variant that returns the identical `config`. The blocks
345
400
  below spell out all options with that module's real entity and action names, so each is
346
- copy-pasteable. `EventAction` comes from `@flusys/nestjs-shared/enums`; the action and
347
- entity constants come from each package's own `config` entry point.
401
+ copy-pasteable. Write the CRUD half as literals - `created`, `updated`, `deleted`,
402
+ `restored`, `purged` never change - and take every other action from its package's own
403
+ `*_EVENT_ACTIONS` constant, so a rename there fails the build instead of silently muting
404
+ the event. Each package exports its entity names the same way (`STORAGE_EVENT_ENTITIES`).
348
405
 
349
406
  In an application, keep the per-module blocks in one map rather than scattering them
350
407
  across registrations - `flusysnest/src/config/modules.config.ts` is the reference:
@@ -355,14 +412,17 @@ const domainEventsEnabled = envConfig.getBoolean('ENABLE_DOMAIN_EVENTS', false);
355
412
  export const moduleEventsConfig: Record<FeatureModuleName, IModuleEventsConfig> = {
356
413
  storage: {
357
414
  enabled: domainEventsEnabled,
358
- actions: [...CRUD_EVENT_ACTIONS, STORAGE_EVENT_ACTIONS.UPLOADED, STORAGE_EVENT_ACTIONS.REMOVED],
359
- entities: [
360
- STORAGE_EVENT_ENTITIES.FILE,
361
- STORAGE_EVENT_ENTITIES.FOLDER,
362
- STORAGE_EVENT_ENTITIES.STORAGE_CONFIG,
415
+ actions: [
416
+ 'file_manager.created',
417
+ 'file_manager.updated',
418
+ 'file_manager.deleted',
419
+ 'file_manager.restored',
420
+ 'file_manager.purged',
421
+ `file_manager.${STORAGE_EVENT_ACTIONS.UPLOADED}`,
422
+ `file_manager.${STORAGE_EVENT_ACTIONS.REMOVED}`,
423
+ 'folder.created',
424
+ // ...one line per entity and action the module can publish
363
425
  ],
364
- excludeEntities: [STORAGE_EVENT_ENTITIES.STORAGE_CONFIG],
365
- includePayload: true,
366
426
  },
367
427
  // ...one entry per feature module
368
428
  };
@@ -374,28 +434,26 @@ export function getBaseModuleConfig(moduleName: FeatureModuleName) {
374
434
  AuthModule.forRoot({ config: { ...authConfig, ...getBaseModuleConfig('auth') } });
375
435
  ```
376
436
 
377
- Spelling `actions` and `entities` out in full is the same as omitting them, and makes the
378
- file a readable inventory of what each module can publish - delete a line to stop
379
- publishing it. The trade-off is that a package gaining a new entity or action stays silent
380
- until it is added here, so keep a test asserting these lists cover each package's own
381
- `*_EVENT_ACTIONS` / `*_EVENT_ENTITIES` constants. One env flag still arms every module, but
382
- each keeps its own block, so narrowing one module never touches the others. Note the two `excludeEntities` entries that ship by
383
- default: `storageConfig` and `emailConfig` rows hold S3, Azure, SFTP and SMTP credentials
384
- in a `config` blob, so they stay off the bus entirely.
437
+ Spelling every `entity.action` pair out in full - CRUD lifecycle ones included, rather
438
+ than spreading a shared array or leaning on bare actions - is the same as omitting the
439
+ option, and makes the file a readable inventory of what each module publishes: delete one
440
+ line to stop publishing that one pair. The trade-off is that a package gaining an entity
441
+ or an action stays silent until it is listed, so keep a test asserting the list covers
442
+ each package's own `*_EVENT_ENTITIES` and `*_EVENT_ACTIONS` constants. One env flag still
443
+ arms every module, but each keeps its own block, so narrowing one module never touches
444
+ the others.
385
445
 
386
446
  #### auth
387
447
 
388
448
  ```typescript
389
- import { AUTH_EVENT_ACTIONS, AUTH_EVENT_ENTITIES } from '@flusys/nestjs-auth/config';
449
+ import { AUTH_EVENT_ACTIONS } from '@flusys/nestjs-auth/config';
390
450
 
391
451
  AuthModule.forRoot({
392
452
  config: {
393
453
  ...dbConfig,
394
454
  events: {
395
455
  enabled: true,
396
- actions: [EventAction.CREATED, AUTH_EVENT_ACTIONS.LOGGED_IN],
397
- entities: [AUTH_EVENT_ENTITIES.USER, AUTH_EVENT_ENTITIES.SESSION],
398
- excludeEntities: ['company_branch'],
456
+ actions: ['appuser.created', `session.${AUTH_EVENT_ACTIONS.LOGGED_IN}`],
399
457
  includePayload: false,
400
458
  },
401
459
  },
@@ -413,17 +471,18 @@ AuthModule.forRoot({
413
471
  #### iam
414
472
 
415
473
  ```typescript
416
- import { IAM_EVENT_ACTIONS, IAM_EVENT_ENTITIES } from '@flusys/nestjs-iam/config';
474
+ import { IAM_EVENT_ACTIONS } from '@flusys/nestjs-iam/config';
417
475
 
418
476
  IAMModule.forRoot({
419
477
  config: {
420
478
  ...dbConfig,
421
479
  events: {
422
480
  enabled: true,
423
- actions: [EventAction.CREATED, EventAction.DELETED, IAM_EVENT_ACTIONS.PERMISSIONS_ASSIGNED],
424
- entities: ['role', IAM_EVENT_ENTITIES.USER_ROLE],
425
- excludeEntities: ['action'],
426
- includePayload: true,
481
+ actions: [
482
+ 'role.created',
483
+ 'role.deleted',
484
+ `user_role.${IAM_EVENT_ACTIONS.PERMISSIONS_ASSIGNED}`,
485
+ ],
427
486
  },
428
487
  },
429
488
  });
@@ -437,17 +496,14 @@ IAMModule.forRoot({
437
496
  #### storage
438
497
 
439
498
  ```typescript
440
- import { STORAGE_EVENT_ACTIONS, STORAGE_EVENT_ENTITIES } from '@flusys/nestjs-storage/config';
499
+ import { STORAGE_EVENT_ACTIONS } from '@flusys/nestjs-storage/config';
441
500
 
442
501
  StorageModule.forRoot({
443
502
  config: {
444
503
  ...dbConfig,
445
504
  events: {
446
505
  enabled: true,
447
- actions: [EventAction.CREATED, EventAction.DELETED, STORAGE_EVENT_ACTIONS.UPLOADED],
448
- entities: [STORAGE_EVENT_ENTITIES.FILE, 'folder'],
449
- excludeEntities: ['storageConfig'],
450
- includePayload: true,
506
+ actions: ['folder.created', 'folder.deleted', `file_manager.${STORAGE_EVENT_ACTIONS.UPLOADED}`],
451
507
  },
452
508
  },
453
509
  });
@@ -472,9 +528,7 @@ FormBuilderModule.forRoot({
472
528
  ...dbConfig,
473
529
  events: {
474
530
  enabled: true,
475
- actions: [FORM_BUILDER_EVENT_ACTIONS.SUBMITTED],
476
- entities: ['form_result'],
477
- excludeEntities: [],
531
+ actions: [`form_result.${FORM_BUILDER_EVENT_ACTIONS.SUBMITTED}`],
478
532
  includePayload: false,
479
533
  },
480
534
  },
@@ -492,16 +546,17 @@ audit consumer can work with `includePayload: false` and never see answer data.
492
546
  #### email
493
547
 
494
548
  ```typescript
495
- import { EMAIL_EVENT_ACTIONS, EMAIL_EVENT_ENTITIES } from '@flusys/nestjs-email/config';
549
+ import { EMAIL_EVENT_ACTIONS } from '@flusys/nestjs-email/config';
496
550
 
497
551
  EmailModule.forRoot({
498
552
  config: {
499
553
  ...dbConfig,
500
554
  events: {
501
555
  enabled: true,
502
- actions: [EMAIL_EVENT_ACTIONS.SENT, EMAIL_EVENT_ACTIONS.FAILED],
503
- entities: [EMAIL_EVENT_ENTITIES.MESSAGE],
504
- excludeEntities: ['emailConfig'],
556
+ actions: [
557
+ `email_message.${EMAIL_EVENT_ACTIONS.SENT}`,
558
+ `email_message.${EMAIL_EVENT_ACTIONS.FAILED}`,
559
+ ],
505
560
  includePayload: false,
506
561
  },
507
562
  },
@@ -524,10 +579,10 @@ EventManagerModule.forRoot({
524
579
  ...dbConfig,
525
580
  events: {
526
581
  enabled: true,
527
- actions: [EventAction.CREATED, EVENT_MANAGER_EVENT_ACTIONS.PARTICIPANT_STATUS_CHANGED],
528
- entities: ['event'],
529
- excludeEntities: [],
530
- includePayload: true,
582
+ actions: [
583
+ 'event.created',
584
+ `event.${EVENT_MANAGER_EVENT_ACTIONS.PARTICIPANT_STATUS_CHANGED}`,
585
+ ],
531
586
  },
532
587
  },
533
588
  });
@@ -543,10 +598,7 @@ The participant event is keyed by the **event** id, with `participantId`,
543
598
  #### notification
544
599
 
545
600
  ```typescript
546
- import {
547
- NOTIFICATION_EVENT_ACTIONS,
548
- NOTIFICATION_EVENT_ENTITIES,
549
- } from '@flusys/nestjs-notification/config';
601
+ import { NOTIFICATION_EVENT_ACTIONS } from '@flusys/nestjs-notification/config';
550
602
 
551
603
  NotificationModule.forRoot({
552
604
  config: {
@@ -554,9 +606,10 @@ NotificationModule.forRoot({
554
606
  enableRealtime: true,
555
607
  events: {
556
608
  enabled: true,
557
- actions: [NOTIFICATION_EVENT_ACTIONS.SENT, NOTIFICATION_EVENT_ACTIONS.READ],
558
- entities: [NOTIFICATION_EVENT_ENTITIES.NOTIFICATION],
559
- excludeEntities: [],
609
+ actions: [
610
+ `notification.${NOTIFICATION_EVENT_ACTIONS.SENT}`,
611
+ `notification.${NOTIFICATION_EVENT_ACTIONS.READ}`,
612
+ ],
560
613
  includePayload: false,
561
614
  },
562
615
  },
@@ -581,9 +634,7 @@ LocalizationModule.forRoot({
581
634
  defaultLanguageCode: 'en',
582
635
  events: {
583
636
  enabled: true,
584
- actions: [LOCALIZATION_EVENT_ACTIONS.BULK_UPDATED],
585
- entities: ['translation'],
586
- excludeEntities: ['translationKey'],
637
+ actions: [`translation.${LOCALIZATION_EVENT_ACTIONS.BULK_UPDATED}`],
587
638
  includePayload: false,
588
639
  },
589
640
  },
@@ -609,10 +660,7 @@ TaskManagerModule.forRoot({
609
660
  ...dbConfig,
610
661
  events: {
611
662
  enabled: true,
612
- actions: [TASK_MANAGER_EVENT_ACTIONS.MOVED, TASK_MANAGER_EVENT_ACTIONS.ASSIGNED],
613
- entities: ['task', 'task_list'],
614
- excludeEntities: ['task_comment'],
615
- includePayload: true,
663
+ actions: [`task.${TASK_MANAGER_EVENT_ACTIONS.MOVED}`, `task.${TASK_MANAGER_EVENT_ACTIONS.ASSIGNED}`],
616
664
  },
617
665
  },
618
666
  });
@@ -13,7 +13,6 @@ const _typeorm = require("typeorm");
13
13
  const _constants = require("../constants");
14
14
  const _logactiondecorator = require("../decorators/log-action.decorator");
15
15
  const _dtos = require("../dtos");
16
- const _eventactionenum = require("../enums/event-action.enum");
17
16
  const _interfaces = require("../interfaces");
18
17
  const _utilsservice = require("../modules/utils/utils.service");
19
18
  const _domaineventpublisher = require("./domain-event.publisher");
@@ -40,30 +39,19 @@ function _ts_decorate(decorators, target, key, desc) {
40
39
  function _ts_metadata(k, v) {
41
40
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
42
41
  }
43
- const DEFAULT_EVENT_MODULE = 'app';
44
- const OPERATION_EVENT_ACTIONS = {
45
- insert: _eventactionenum.EventAction.CREATED,
46
- insertMany: _eventactionenum.EventAction.CREATED,
47
- update: _eventactionenum.EventAction.UPDATED,
48
- updateMany: _eventactionenum.EventAction.UPDATED,
49
- bulkUpsert: _eventactionenum.EventAction.UPDATED
50
- };
51
42
  const DELETE_EVENT_ACTIONS = {
52
- delete: _eventactionenum.EventAction.DELETED,
53
- restore: _eventactionenum.EventAction.RESTORED
43
+ delete: 'deleted',
44
+ restore: 'restored',
45
+ permanent: 'purged'
54
46
  };
55
47
  let ApiService = class ApiService {
56
- resolveEntity() {
57
- return this._entity;
58
- }
59
48
  getDataSourceProvider() {
60
49
  return this._dataSourceProvider;
61
50
  }
62
51
  async ensureDataSourceRepository() {
63
52
  if (!this.repositoryInitialized) {
64
- const entity = this.resolveEntity();
65
53
  const provider = this.getDataSourceProvider();
66
- this.repository = await provider.getRepository(entity);
54
+ this.repository = await provider.getRepository(this._entity);
67
55
  this.repositoryInitialized = true;
68
56
  }
69
57
  }
@@ -75,11 +63,8 @@ let ApiService = class ApiService {
75
63
  }
76
64
  return repositories;
77
65
  }
78
- async getDataSourceForService() {
79
- return this.getDataSourceProvider().getDataSource();
80
- }
81
66
  async insert(dto, user) {
82
- return this.executeInTransaction('insert', async (qr)=>{
67
+ return this.executeInTransaction('created', async (qr)=>{
83
68
  await this.beforeInsertOperation(dto, user, qr);
84
69
  const entities = await this.convertRequestDtoToEntity(dto, user);
85
70
  const saved = await qr.manager.save(this.repository.target, entities);
@@ -91,7 +76,7 @@ let ApiService = class ApiService {
91
76
  }, user);
92
77
  }
93
78
  async insertMany(dtos, user) {
94
- return this.executeInTransaction('insertMany', async (qr)=>{
79
+ return this.executeInTransaction('created', async (qr)=>{
95
80
  await this.beforeInsertOperation(dtos, user, qr);
96
81
  const entities = await this.convertRequestDtoToEntity(dtos, user);
97
82
  const saved = await qr.manager.save(this.repository.target, entities);
@@ -103,7 +88,7 @@ let ApiService = class ApiService {
103
88
  }, user);
104
89
  }
105
90
  async update(dto, user) {
106
- return this.executeInTransaction('update', async (qr)=>{
91
+ return this.executeInTransaction('updated', async (qr)=>{
107
92
  await this.beforeUpdateOperation(dto, user, qr);
108
93
  const entities = await this.convertRequestDtoToEntity(dto, user);
109
94
  const saved = await qr.manager.save(this.repository.target, entities);
@@ -115,7 +100,7 @@ let ApiService = class ApiService {
115
100
  }, user);
116
101
  }
117
102
  async updateMany(dtos, user) {
118
- return this.executeInTransaction('updateMany', async (qr)=>{
103
+ return this.executeInTransaction('updated', async (qr)=>{
119
104
  await this.beforeUpdateOperation(dtos, user, qr);
120
105
  const entities = await this.convertRequestDtoToEntity(dtos, user);
121
106
  const saved = await qr.manager.save(this.repository.target, entities);
@@ -127,7 +112,7 @@ let ApiService = class ApiService {
127
112
  }, user);
128
113
  }
129
114
  async bulkUpsert(toInsert, toUpdate, user) {
130
- return this.executeInTransaction('bulkUpsert', async (qr)=>{
115
+ return this.executeInTransaction(undefined, async (qr)=>{
131
116
  const allResults = [];
132
117
  const eventGroups = [];
133
118
  if (toInsert.length > 0) {
@@ -137,7 +122,7 @@ let ApiService = class ApiService {
137
122
  await this.afterInsertOperation(this.ensureArray(insertedSaved), toInsert, user, qr);
138
123
  allResults.push(...this.ensureArray(insertedSaved));
139
124
  eventGroups.push({
140
- action: _eventactionenum.EventAction.CREATED,
125
+ action: 'created',
141
126
  entities: this.ensureArray(insertedSaved)
142
127
  });
143
128
  }
@@ -148,7 +133,7 @@ let ApiService = class ApiService {
148
133
  await this.afterUpdateOperation(this.ensureArray(updatedSaved), toUpdate, user, qr);
149
134
  allResults.push(...this.ensureArray(updatedSaved));
150
135
  eventGroups.push({
151
- action: _eventactionenum.EventAction.UPDATED,
136
+ action: 'updated',
152
137
  entities: this.ensureArray(updatedSaved)
153
138
  });
154
139
  }
@@ -184,7 +169,7 @@ let ApiService = class ApiService {
184
169
  if (error instanceof _common.NotFoundException) {
185
170
  throw error;
186
171
  }
187
- this.handleError(error, 'findByIds');
172
+ this.handleError(error);
188
173
  }
189
174
  }
190
175
  async findById(id, user, select) {
@@ -224,7 +209,7 @@ let ApiService = class ApiService {
224
209
  if (error instanceof _common.NotFoundException) {
225
210
  throw error;
226
211
  }
227
- this.handleError(error, 'findById');
212
+ this.handleError(error);
228
213
  }
229
214
  }
230
215
  async getAll(search, filterAndPaginationDto, user) {
@@ -323,7 +308,7 @@ let ApiService = class ApiService {
323
308
  }
324
309
  return finalResponse;
325
310
  } catch (error) {
326
- this.handleError(error, 'getAll');
311
+ this.handleError(error);
327
312
  }
328
313
  }
329
314
  async delete(option, user) {
@@ -361,7 +346,7 @@ let ApiService = class ApiService {
361
346
  ]);
362
347
  await this.publishLifecycleEvents([
363
348
  {
364
- action: DELETE_EVENT_ACTIONS[option.type] ?? _eventactionenum.EventAction.PURGED,
349
+ action: DELETE_EVENT_ACTIONS[option.type],
365
350
  entities: ids,
366
351
  idsOnly: true
367
352
  }
@@ -369,7 +354,7 @@ let ApiService = class ApiService {
369
354
  return null;
370
355
  } catch (error) {
371
356
  await queryRunner.rollbackTransaction();
372
- this.handleError(error, 'delete');
357
+ this.handleError(error);
373
358
  } finally{
374
359
  await queryRunner.release();
375
360
  }
@@ -378,7 +363,7 @@ let ApiService = class ApiService {
378
363
  for (const group of groups){
379
364
  if (!group.entities.length) continue;
380
365
  await (0, _domaineventpublisher.publishDomainEvent)({
381
- module: this.moduleName ?? DEFAULT_EVENT_MODULE,
366
+ module: this.moduleName,
382
367
  entity: this.entityName,
383
368
  action: group.action,
384
369
  ids: group.entities.map((entity)=>entity.id),
@@ -395,7 +380,7 @@ let ApiService = class ApiService {
395
380
  */ async publishDomainAction(action, input = {}) {
396
381
  await (0, _domaineventpublisher.publishDomainEvent)({
397
382
  ...input,
398
- module: this.moduleName ?? DEFAULT_EVENT_MODULE,
383
+ module: this.moduleName,
399
384
  entity: this.entityName,
400
385
  action
401
386
  });
@@ -406,7 +391,7 @@ let ApiService = class ApiService {
406
391
  async clearCacheForId(entities) {
407
392
  await Promise.all(entities.map((item)=>this.utilsService.clearCache(this.entityName, this.cacheManager, item.id)));
408
393
  }
409
- handleError(error, _operation) {
394
+ handleError(error) {
410
395
  if (error instanceof Error) {
411
396
  throw error;
412
397
  }
@@ -420,7 +405,11 @@ let ApiService = class ApiService {
420
405
  value
421
406
  ];
422
407
  }
423
- async executeInTransaction(operation, fn, user) {
408
+ /**
409
+ * `action` is the lifecycle action to publish for the whole batch; an
410
+ * operation that saves more than one kind of row - a bulk upsert - passes
411
+ * `undefined` and returns its own `eventGroups` instead.
412
+ */ async executeInTransaction(action, fn, user) {
424
413
  await this.ensureDataSourceRepository();
425
414
  const qr = this.repository.manager.connection.createQueryRunner();
426
415
  await qr.connect();
@@ -435,19 +424,19 @@ let ApiService = class ApiService {
435
424
  this.clearCacheForId(savedArray)
436
425
  ]);
437
426
  }
438
- await this.publishLifecycleEvents(eventGroups ?? [
427
+ await this.publishLifecycleEvents(eventGroups ?? (action ? [
439
428
  {
440
- action: OPERATION_EVENT_ACTIONS[operation],
429
+ action,
441
430
  entities: savedArray
442
431
  }
443
- ], user ?? null);
432
+ ] : []), user ?? null);
444
433
  if (returnFirst) {
445
434
  return this.convertEntityToResponseDto(savedArray[0], false);
446
435
  }
447
436
  return savedArray.map((e)=>this.convertEntityToResponseDto(e, false));
448
437
  } catch (error) {
449
438
  await qr.rollbackTransaction();
450
- this.handleError(error, operation);
439
+ this.handleError(error);
451
440
  } finally{
452
441
  await qr.release();
453
442
  }
@@ -520,7 +509,7 @@ let ApiService = class ApiService {
520
509
  return Object.assign(existing, d);
521
510
  }
522
511
  delete d.id;
523
- return Object.assign(await this.getEntityClass(), d);
512
+ return Object.assign(this.repository.create(), d);
524
513
  }
525
514
  async convertArrayDtoToEntities(dtos, user) {
526
515
  return await Promise.all(dtos.map((dto)=>this.convertSingleDtoToEntity(dto, user)));
@@ -531,10 +520,11 @@ let ApiService = class ApiService {
531
520
  convertEntityListToResponseListDto(entities, _isRaw) {
532
521
  return entities.map((entity)=>this.convertEntityToResponseDto(entity, _isRaw));
533
522
  }
534
- async getEntityClass() {
535
- return this.repository.create();
536
- }
537
- constructor(entityName, cacheManager, utilsService, _loggerName, isCacheable = false, moduleName, entity, dataSourceProvider){
523
+ /**
524
+ * `entity` is the table this service reads and writes. A service whose table
525
+ * depends on runtime config - the company feature, say - passes the ternary
526
+ * here, reading the injected config through the constructor parameter.
527
+ */ constructor(entityName, cacheManager, utilsService, _loggerName, isCacheable = false, moduleName, dataSourceProvider, entity){
538
528
  _define_property(this, "entityName", void 0);
539
529
  _define_property(this, "cacheManager", void 0);
540
530
  _define_property(this, "utilsService", void 0);
@@ -552,8 +542,8 @@ let ApiService = class ApiService {
552
542
  this.isCacheable = isCacheable;
553
543
  this.moduleName = moduleName;
554
544
  this.repositoryInitialized = false;
555
- this._entity = entity;
556
545
  this._dataSourceProvider = dataSourceProvider;
546
+ this._entity = entity;
557
547
  }
558
548
  };
559
549
  _ts_decorate([
@@ -635,7 +625,7 @@ ApiService = _ts_decorate([
635
625
  String,
636
626
  Boolean,
637
627
  String,
638
- typeof _typeorm.EntityTarget === "undefined" ? Object : _typeorm.EntityTarget,
639
- typeof _interfaces.IDataSourceProvider === "undefined" ? Object : _interfaces.IDataSourceProvider
628
+ typeof _interfaces.IDataSourceProvider === "undefined" ? Object : _interfaces.IDataSourceProvider,
629
+ typeof _typeorm.EntityTarget === "undefined" ? Object : _typeorm.EntityTarget
640
630
  ])
641
631
  ], ApiService);