@solidstarters/solid-core 1.2.57 → 1.2.58

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 (93) hide show
  1. package/dist/controllers/chatter-message-details.controller.d.ts +41 -0
  2. package/dist/controllers/chatter-message-details.controller.d.ts.map +1 -0
  3. package/dist/controllers/chatter-message-details.controller.js +179 -0
  4. package/dist/controllers/chatter-message-details.controller.js.map +1 -0
  5. package/dist/controllers/chatter-message.controller.d.ts +44 -0
  6. package/dist/controllers/chatter-message.controller.d.ts.map +1 -0
  7. package/dist/controllers/chatter-message.controller.js +199 -0
  8. package/dist/controllers/chatter-message.controller.js.map +1 -0
  9. package/dist/dtos/create-chatter-message-details.dto.d.ts +10 -0
  10. package/dist/dtos/create-chatter-message-details.dto.d.ts.map +1 -0
  11. package/dist/dtos/create-chatter-message-details.dto.js +66 -0
  12. package/dist/dtos/create-chatter-message-details.dto.js.map +1 -0
  13. package/dist/dtos/create-chatter-message.dto.d.ts +10 -0
  14. package/dist/dtos/create-chatter-message.dto.d.ts.map +1 -0
  15. package/dist/dtos/create-chatter-message.dto.js +65 -0
  16. package/dist/dtos/create-chatter-message.dto.js.map +1 -0
  17. package/dist/dtos/create-field-metadata.dto.d.ts +1 -0
  18. package/dist/dtos/create-field-metadata.dto.d.ts.map +1 -1
  19. package/dist/dtos/create-field-metadata.dto.js +6 -1
  20. package/dist/dtos/create-field-metadata.dto.js.map +1 -1
  21. package/dist/dtos/post-chatter-message.dto.d.ts +7 -0
  22. package/dist/dtos/post-chatter-message.dto.d.ts.map +1 -0
  23. package/dist/dtos/post-chatter-message.dto.js +41 -0
  24. package/dist/dtos/post-chatter-message.dto.js.map +1 -0
  25. package/dist/dtos/update-chatter-message-details.dto.d.ts +11 -0
  26. package/dist/dtos/update-chatter-message-details.dto.d.ts.map +1 -0
  27. package/dist/dtos/update-chatter-message-details.dto.js +70 -0
  28. package/dist/dtos/update-chatter-message-details.dto.js.map +1 -0
  29. package/dist/dtos/update-chatter-message.dto.d.ts +11 -0
  30. package/dist/dtos/update-chatter-message.dto.d.ts.map +1 -0
  31. package/dist/dtos/update-chatter-message.dto.js +74 -0
  32. package/dist/dtos/update-chatter-message.dto.js.map +1 -0
  33. package/dist/entities/chatter-message-details.entity.d.ts +11 -0
  34. package/dist/entities/chatter-message-details.entity.d.ts.map +1 -0
  35. package/dist/entities/chatter-message-details.entity.js +52 -0
  36. package/dist/entities/chatter-message-details.entity.js.map +1 -0
  37. package/dist/entities/chatter-message.entity.d.ts +11 -0
  38. package/dist/entities/chatter-message.entity.d.ts.map +1 -0
  39. package/dist/entities/chatter-message.entity.js +53 -0
  40. package/dist/entities/chatter-message.entity.js.map +1 -0
  41. package/dist/entities/field-metadata.entity.d.ts +1 -0
  42. package/dist/entities/field-metadata.entity.d.ts.map +1 -1
  43. package/dist/entities/field-metadata.entity.js +5 -1
  44. package/dist/entities/field-metadata.entity.js.map +1 -1
  45. package/dist/index.d.ts +10 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +10 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/seeders/seed-data/solid-core-metadata.json +517 -1
  50. package/dist/services/chatter-message-details.service.d.ts +24 -0
  51. package/dist/services/chatter-message-details.service.d.ts.map +1 -0
  52. package/dist/services/chatter-message-details.service.js +59 -0
  53. package/dist/services/chatter-message-details.service.js.map +1 -0
  54. package/dist/services/chatter-message.service.d.ts +37 -0
  55. package/dist/services/chatter-message.service.d.ts.map +1 -0
  56. package/dist/services/chatter-message.service.js +279 -0
  57. package/dist/services/chatter-message.service.js.map +1 -0
  58. package/dist/services/crud.service.d.ts +3 -1
  59. package/dist/services/crud.service.d.ts.map +1 -1
  60. package/dist/services/crud.service.js +43 -3
  61. package/dist/services/crud.service.js.map +1 -1
  62. package/dist/services/user-context.service.d.ts +10 -0
  63. package/dist/services/user-context.service.d.ts.map +1 -0
  64. package/dist/services/user-context.service.js +42 -0
  65. package/dist/services/user-context.service.js.map +1 -0
  66. package/dist/solid-core.module.d.ts.map +1 -1
  67. package/dist/solid-core.module.js +18 -2
  68. package/dist/solid-core.module.js.map +1 -1
  69. package/dist/subscribers/audit.subscriber.d.ts +17 -0
  70. package/dist/subscribers/audit.subscriber.d.ts.map +1 -0
  71. package/dist/subscribers/audit.subscriber.js +83 -0
  72. package/dist/subscribers/audit.subscriber.js.map +1 -0
  73. package/dist/tsconfig.tsbuildinfo +1 -1
  74. package/package.json +1 -1
  75. package/src/controllers/chatter-message-details.controller.ts +92 -0
  76. package/src/controllers/chatter-message.controller.ts +105 -0
  77. package/src/dtos/create-chatter-message-details.dto.ts +40 -0
  78. package/src/dtos/create-chatter-message.dto.ts +34 -0
  79. package/src/dtos/create-field-metadata.dto.ts +4 -0
  80. package/src/dtos/post-chatter-message.dto.ts +19 -0
  81. package/src/dtos/update-chatter-message-details.dto.ts +43 -0
  82. package/src/dtos/update-chatter-message.dto.ts +41 -0
  83. package/src/entities/chatter-message-details.entity.ts +25 -0
  84. package/src/entities/chatter-message.entity.ts +22 -0
  85. package/src/entities/field-metadata.entity.ts +3 -0
  86. package/src/index.ts +10 -2
  87. package/src/seeders/seed-data/solid-core-metadata.json +518 -2
  88. package/src/services/chatter-message-details.service.ts +34 -0
  89. package/src/services/chatter-message.service.ts +301 -0
  90. package/src/services/crud.service.ts +10 -2
  91. package/src/services/user-context.service.ts +31 -0
  92. package/src/solid-core.module.ts +18 -3
  93. package/src/subscribers/audit.subscriber.ts +73 -0
@@ -0,0 +1,301 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
3
+ import { DiscoveryService, ModuleRef } from "@nestjs/core";
4
+ import { EntityManager, Repository, EntityMetadata } from 'typeorm';
5
+
6
+ import { CRUDService } from 'src/services/crud.service';
7
+ import { ModelMetadataService } from 'src/services/model-metadata.service';
8
+ import { ModuleMetadataService } from 'src/services/module-metadata.service';
9
+ import { ConfigService } from '@nestjs/config';
10
+ import { FileService } from 'src/services/file.service';
11
+ import { CrudHelperService } from 'src/services/crud-helper.service';
12
+ import { PostChatterMessageDto } from 'src/dtos/post-chatter-message.dto';
13
+ import { SolidRequestContextDto } from 'src/dtos/solid-request-context.dto';
14
+ import { ChatterMessage } from '../entities/chatter-message.entity';
15
+ import { getMediaStorageProvider } from './mediaStorageProviders';
16
+ import { MediaStorageProviderType } from '../dtos/create-media-storage-provider-metadata.dto';
17
+ import { ChatterMessageDetails } from '../entities/chatter-message-details.entity';
18
+ import { ModelMetadata } from 'src/entities/model-metadata.entity';
19
+ import { UserContextService } from './user-context.service';
20
+ @Injectable()
21
+ export class ChatterMessageService extends CRUDService<ChatterMessage>{
22
+ constructor(
23
+ readonly modelMetadataService: ModelMetadataService,
24
+ readonly moduleMetadataService: ModuleMetadataService,
25
+ readonly configService: ConfigService,
26
+ readonly fileService: FileService,
27
+ readonly discoveryService: DiscoveryService,
28
+ readonly crudHelperService: CrudHelperService,
29
+ @InjectEntityManager()
30
+ readonly entityManager: EntityManager,
31
+ @InjectRepository(ChatterMessage, 'default')
32
+ readonly repo: Repository<ChatterMessage>,
33
+ @InjectRepository(ChatterMessageDetails, 'default')
34
+ readonly chatterMessageDetailsRepo: Repository<ChatterMessageDetails>,
35
+ readonly moduleRef: ModuleRef,
36
+ @InjectRepository(ModelMetadata)
37
+ private readonly modelMetadataRepo: Repository<ModelMetadata>,
38
+ readonly userContextService: UserContextService
39
+ ) {
40
+ super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService,entityManager, repo, 'chatterMessage', 'solid-core', moduleRef, userContextService);
41
+ }
42
+
43
+ async postMessage(postDto: PostChatterMessageDto, solidRequestContext: SolidRequestContextDto, files: Express.Multer.File[] = []) {
44
+ const chatterMessage = new ChatterMessage();
45
+ chatterMessage.messageType = 'custom';
46
+ chatterMessage.messageSubType = postDto.messageSubType || 'general';
47
+ chatterMessage.messageBody = postDto.messageBody;
48
+ chatterMessage.coModelEntityId = postDto.coModelEntityId;
49
+ chatterMessage.coModelName = postDto.coModelName;
50
+
51
+ const userId = typeof solidRequestContext.activeUser === 'object'
52
+ ? solidRequestContext.activeUser.sub
53
+ : solidRequestContext.activeUser;
54
+
55
+ chatterMessage.user = { id: userId } as any;
56
+
57
+ const savedMessage = await this.repo.save(chatterMessage);
58
+
59
+ if (files && files.length > 0) {
60
+ const model = await this.modelMetadataService.findOneBySingularName('chatterMessage', {
61
+ fields: {
62
+ model: true,
63
+ mediaStorageProvider: true,
64
+ },
65
+ module: true,
66
+ });
67
+
68
+ const mediaFields = model.fields.filter(field => field.type === 'mediaSingle' || field.type === 'mediaMultiple');
69
+
70
+ for (const mediaField of mediaFields) {
71
+ const media = files.filter(multerFile => multerFile.fieldname === mediaField.name);
72
+ if (media.length > 0) {
73
+ const storageProviderMetadata = mediaField.mediaStorageProvider;
74
+ const storageProviderType = storageProviderMetadata.type as MediaStorageProviderType;
75
+ const storageProvider = await getMediaStorageProvider(this.moduleRef, storageProviderType);
76
+ await storageProvider.store(media, savedMessage, mediaField);
77
+ }
78
+ }
79
+ }
80
+
81
+ return savedMessage;
82
+ }
83
+
84
+ async postAuditMessageOnInsert(entity: any, metadata: EntityMetadata, activeUser: any, messageQueue: boolean = false) {
85
+ const model = await this.modelMetadataRepo.findOne({
86
+ where: {
87
+ displayName: metadata.name
88
+ },
89
+ relations: {
90
+ fields: true,
91
+ module: true
92
+ }
93
+ });
94
+
95
+ if (!model || !model.enableAuditTracking) {
96
+ return;
97
+ }
98
+
99
+ const auditFields = model.fields.filter(field =>
100
+ field.enableAuditTracking &&
101
+ !['oneToMany', 'mediaSingle', 'mediaMultiple', 'computed', 'richText', 'json'].includes(field.type)
102
+ );
103
+
104
+ const chatterMessage = new ChatterMessage();
105
+ chatterMessage.messageType = 'audit';
106
+ chatterMessage.messageSubType = 'insert';
107
+ chatterMessage.coModelEntityId = entity.id;
108
+ chatterMessage.coModelName = model.singularName;
109
+ chatterMessage.messageBody = `New ${model.displayName} created`;
110
+
111
+ if (activeUser) {
112
+ const userId = typeof activeUser === 'object' ? activeUser.sub : activeUser;
113
+ chatterMessage.user = { id: userId } as any;
114
+ } else {
115
+ chatterMessage.user = null;
116
+ }
117
+
118
+ const savedMessage = await this.repo.save(chatterMessage);
119
+
120
+ for (const field of auditFields) {
121
+ const fieldValue = entity[field.name];
122
+ if (fieldValue !== undefined && fieldValue !== null) {
123
+ const messageDetail = new ChatterMessageDetails();
124
+ messageDetail.chatterMessage = savedMessage;
125
+ messageDetail.fieldName = field.name;
126
+ messageDetail.oldValue = null;
127
+ messageDetail.oldValueDisplay = null;
128
+ messageDetail.newValue = this.formatFieldValue(field, fieldValue);
129
+ messageDetail.newValueDisplay = this.formatFieldValueDisplay(field, fieldValue);
130
+ await this.chatterMessageDetailsRepo.save(messageDetail);
131
+ }
132
+ }
133
+ }
134
+
135
+ async postAuditMessageOnUpdate(entity: any, metadata: EntityMetadata, databaseEntity: any, activeUser: any, messageQueue: boolean = false) {
136
+ const model = await this.modelMetadataRepo.findOne({
137
+ where: {
138
+ displayName: metadata.name
139
+ },
140
+ relations: {
141
+ fields: true,
142
+ module: true
143
+ }
144
+ });
145
+
146
+ if (!model || !model.enableAuditTracking) {
147
+ return;
148
+ }
149
+
150
+ const auditFields = model.fields.filter(field =>
151
+ field.enableAuditTracking &&
152
+ !['oneToMany', 'mediaSingle', 'mediaMultiple', 'computed', 'richText', 'json'].includes(field.type)
153
+ );
154
+
155
+ const relationFields = auditFields.filter(field =>
156
+ field.type === 'relation'
157
+ );
158
+ if (relationFields.length > 0) {
159
+ const populatedEntity = await this.entityManager.findOne(metadata.target, {
160
+ where: { id: databaseEntity.id },
161
+ relations: relationFields.map(field => field.name)
162
+ });
163
+ if (populatedEntity) {
164
+ databaseEntity = populatedEntity;
165
+ }
166
+ }
167
+
168
+ const changedFields = auditFields.filter(field => {
169
+ const newValue = entity[field.name];
170
+ const oldValue = databaseEntity[field.name];
171
+ return this.hasValueChanged(newValue, oldValue);
172
+ });
173
+
174
+ if (changedFields.length === 0) {
175
+ return;
176
+ }
177
+
178
+ const chatterMessage = new ChatterMessage();
179
+ chatterMessage.messageType = 'audit';
180
+ chatterMessage.messageSubType = 'update';
181
+ chatterMessage.coModelEntityId = entity.id;
182
+ chatterMessage.coModelName = model.singularName;
183
+ chatterMessage.messageBody = `${model.displayName} updated`;
184
+
185
+ if (activeUser) {
186
+ const userId = typeof activeUser === 'object' ? activeUser.sub : activeUser;
187
+ chatterMessage.user = { id: userId } as any;
188
+ } else {
189
+ chatterMessage.user = null;
190
+ }
191
+
192
+ const savedMessage = await this.repo.save(chatterMessage);
193
+
194
+ for (const field of changedFields) {
195
+ const messageDetail = new ChatterMessageDetails();
196
+ messageDetail.chatterMessage = savedMessage;
197
+ messageDetail.fieldName = field.name;
198
+ messageDetail.oldValue = this.formatFieldValue(field, databaseEntity[field.name]);
199
+ messageDetail.newValue = this.formatFieldValue(field, entity[field.name]);
200
+ messageDetail.oldValueDisplay = this.formatFieldValueDisplay(field, databaseEntity[field.name]);
201
+ messageDetail.newValueDisplay = this.formatFieldValueDisplay(field, entity[field.name]);
202
+ await this.chatterMessageDetailsRepo.save(messageDetail);
203
+ }
204
+ }
205
+
206
+ async postAuditMessageOnDelete(entity: any, metadata: EntityMetadata, databaseEntity: any, activeUser: any, messageQueue: boolean = false) {
207
+ const model = await this.modelMetadataRepo.findOne({
208
+ where: {
209
+ displayName: metadata.name
210
+ },
211
+ relations: {
212
+ fields: true,
213
+ module: true
214
+ }
215
+ });
216
+
217
+ if (!model || !model.enableAuditTracking) {
218
+ return;
219
+ }
220
+
221
+ const chatterMessage = new ChatterMessage();
222
+ chatterMessage.messageType = 'audit';
223
+ chatterMessage.messageSubType = 'delete';
224
+ chatterMessage.coModelEntityId = databaseEntity.id;
225
+ chatterMessage.coModelName = model.singularName;
226
+ chatterMessage.messageBody = `${model.displayName} deleted`;
227
+
228
+ if (activeUser) {
229
+ const userId = typeof activeUser === 'object' ? activeUser.sub : activeUser;
230
+ chatterMessage.user = { id: userId } as any;
231
+ } else {
232
+ chatterMessage.user = null;
233
+ }
234
+
235
+ await this.repo.save(chatterMessage);
236
+ }
237
+
238
+ private formatFieldValue(field: any, value: any): string {
239
+ if (value === null || value === undefined) {
240
+ return '';
241
+ }
242
+
243
+ if (field.type === 'selectionStatic' || field.type === 'selectionDynamic') {
244
+ return `${value}`;
245
+ }
246
+
247
+ if (field.type === 'relation') {
248
+ if (field.relationType === "many-to-one") {
249
+ return value.id;
250
+ }
251
+ if (field.relationType === 'manyToMany') {
252
+ return value.map(item => item.id).join(', ');
253
+ }
254
+ }
255
+
256
+
257
+ return value.toString();
258
+ }
259
+
260
+ private formatFieldValueDisplay(field: any, value: any): string {
261
+ if (value === null || value === undefined) {
262
+ return '';
263
+ }
264
+
265
+ if (field.type === 'selectionStatic' || field.type === 'selectionDynamic') {
266
+ return `${value}`;
267
+ }
268
+
269
+ if (field.type === 'relation') {
270
+ if (field.relationType === "many-to-one") {
271
+ return value.name;
272
+ }
273
+ if (field.relationType === 'many-toMany') {
274
+ return value.map(item => item.name).join(', ');
275
+ }
276
+ }
277
+
278
+
279
+ return value.toString();
280
+ }
281
+
282
+ private hasValueChanged(newValue: any, oldValue: any): boolean {
283
+ if (newValue === oldValue) {
284
+ return false;
285
+ }
286
+
287
+ if (newValue === null && oldValue === null) {
288
+ return false;
289
+ }
290
+
291
+ if (newValue === undefined && oldValue === undefined) {
292
+ return false;
293
+ }
294
+
295
+ if (Array.isArray(newValue) && Array.isArray(oldValue)) {
296
+ return JSON.stringify(newValue) !== JSON.stringify(oldValue);
297
+ }
298
+
299
+ return true;
300
+ }
301
+ }
@@ -34,7 +34,8 @@ import { FileService } from "./file.service";
34
34
  import { getMediaStorageProvider } from "./mediaStorageProviders";
35
35
  import { ModelMetadataService } from "./model-metadata.service";
36
36
  import { ModuleMetadataService } from "./module-metadata.service";
37
-
37
+ import { UserContextService } from "./user-context.service";
38
+ import { Optional } from "@nestjs/common";
38
39
  const DEFAULT_LIMIT = 10;
39
40
  const DEFAULT_OFFSET = 0;
40
41
  export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDto, so we get the proper types in our service
@@ -50,7 +51,8 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
50
51
  readonly repo: Repository<T>,
51
52
  readonly modelName: string,
52
53
  readonly moduleName: string,
53
- readonly moduleRef: ModuleRef
54
+ readonly moduleRef: ModuleRef,
55
+ @Optional() readonly userContextService?: UserContextService
54
56
  //We can just have the Model Entity here
55
57
  ) { }
56
58
 
@@ -66,6 +68,9 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
66
68
  // Check wheather user has create permission for model
67
69
  if (solidRequestContext.activeUser) {
68
70
  const hasPermission = this.crudHelperService.hasCreatePermissionOnModel(solidRequestContext.activeUser, model.singularName);
71
+ if (this.userContextService) {
72
+ this.userContextService.setUser(solidRequestContext.activeUser);
73
+ }
69
74
  if (!hasPermission) {
70
75
  throw new BadRequestException('Forbidden');
71
76
  }
@@ -160,6 +165,9 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
160
165
  // Check wheather user has update permission for model
161
166
  if (solidRequestContext.activeUser) {
162
167
  const hasPermission = this.crudHelperService.hasUpdatePermissionOnModel(solidRequestContext.activeUser, model.singularName);
168
+ if (this.userContextService) {
169
+ this.userContextService.setUser(solidRequestContext.activeUser);
170
+ }
163
171
  if (!hasPermission) {
164
172
  throw new BadRequestException('Forbidden');
165
173
  }
@@ -0,0 +1,31 @@
1
+ import { Injectable, Logger } from '@nestjs/common';
2
+ import { ActiveUserData } from '../interfaces/active-user-data.interface';
3
+
4
+ @Injectable()
5
+ export class UserContextService {
6
+ private readonly logger = new Logger(UserContextService.name);
7
+ private static currentUser: any = null;
8
+
9
+ setUser(user: any) {
10
+ this.logger.debug(`Setting user: ${JSON.stringify(user)}`);
11
+ UserContextService.currentUser = user;
12
+ }
13
+
14
+ getUser() {
15
+ return UserContextService.currentUser;
16
+ }
17
+
18
+ runWithUser<T>(user: any, callback: () => T): T {
19
+ const previousUser = this.getUser();
20
+ try {
21
+ this.setUser(user);
22
+ return callback();
23
+ } finally {
24
+ this.setUser(previousUser);
25
+ }
26
+ }
27
+
28
+ getCurrentUser(): ActiveUserData | null {
29
+ return this.getUser();
30
+ }
31
+ }
@@ -154,7 +154,14 @@ import { RequestContextService } from './services/request-context.service';
154
154
  import { SecurityRuleRepository } from './repository/security-rule.repository';
155
155
  import { SecurityRuleSubscriber } from './subscribers/security-rule.subscriber';
156
156
  import { ListOfValuesController } from './controllers/list-of-values.controller';
157
-
157
+ import { ChatterMessage } from './entities/chatter-message.entity';
158
+ import { ChatterMessageService } from './services/chatter-message.service';
159
+ import { ChatterMessageController } from './controllers/chatter-message.controller';
160
+ import { ChatterMessageDetails } from './entities/chatter-message-details.entity';
161
+ import { ChatterMessageDetailsService } from './services/chatter-message-details.service';
162
+ import { ChatterMessageDetailsController } from './controllers/chatter-message-details.controller';
163
+ import { AuditSubscriber } from './subscribers/audit.subscriber';
164
+ import { UserContextService } from './services/user-context.service';
158
165
 
159
166
  @Global()
160
167
  @Module({
@@ -203,6 +210,8 @@ import { ListOfValuesController } from './controllers/list-of-values.controller'
203
210
  TypeOrmModule.forFeature([SecurityRule]),
204
211
  TypeOrmModule.forFeature([SavedFilters]),
205
212
  TypeOrmModule.forFeature([ListOfValues]),
213
+ TypeOrmModule.forFeature([ChatterMessage]),
214
+ TypeOrmModule.forFeature([ChatterMessageDetails]),
206
215
  // TypeOrmModule.forFeature([User]),
207
216
  ],
208
217
  controllers: [
@@ -232,7 +241,9 @@ import { ListOfValuesController } from './controllers/list-of-values.controller'
232
241
  UserViewMetadataController,
233
242
  SecurityRuleController,
234
243
  SavedFiltersController,
235
- ListOfValuesController
244
+ ListOfValuesController,
245
+ ChatterMessageController,
246
+ ChatterMessageDetailsController
236
247
  ],
237
248
  providers: [
238
249
  {
@@ -333,7 +344,11 @@ import { ListOfValuesController } from './controllers/list-of-values.controller'
333
344
  SecurityRuleRepository,
334
345
  SecurityRuleSubscriber,
335
346
  RequestContextService,
336
- SavedFiltersService
347
+ SavedFiltersService,
348
+ ChatterMessageService,
349
+ ChatterMessageDetailsService,
350
+ AuditSubscriber,
351
+ UserContextService,
337
352
  ],
338
353
  exports: [
339
354
  ModuleMetadataService,
@@ -0,0 +1,73 @@
1
+ import { Connection, EntitySubscriberInterface, EventSubscriber, InsertEvent, RemoveEvent, UpdateEvent } from 'typeorm';
2
+ import { ChatterMessageService } from '../services/chatter-message.service';
3
+ import { EntityMetadata } from 'typeorm';
4
+ import { InjectRepository } from '@nestjs/typeorm';
5
+ import { ModelMetadata } from '../entities/model-metadata.entity';
6
+ import { Repository } from 'typeorm';
7
+ import { Injectable } from '@nestjs/common';
8
+ import { UserContextService } from '../services/user-context.service';
9
+ @Injectable()
10
+ @EventSubscriber()
11
+ export class AuditSubscriber implements EntitySubscriberInterface {
12
+
13
+ constructor(
14
+ private readonly connection: Connection,
15
+ private readonly chatterMessageService: ChatterMessageService,
16
+ @InjectRepository(ModelMetadata)
17
+ private readonly modelMetadataRepo: Repository<ModelMetadata>,
18
+ private readonly userContextService: UserContextService
19
+ ) {
20
+ connection.subscribers.push(this);
21
+ }
22
+
23
+ private async shouldTrackAudit(entity: any, metadata: EntityMetadata): Promise<boolean> {
24
+ const model = await this.modelMetadataRepo.findOne({
25
+ where: {
26
+ displayName: metadata.name
27
+ },
28
+ relations: {
29
+ fields: true,
30
+ module: true
31
+ }
32
+ });
33
+
34
+ if (!model || !model.enableAuditTracking) {
35
+ return false;
36
+ }
37
+
38
+ const auditFields = model.fields.filter(field =>
39
+ field.enableAuditTracking &&
40
+ !['oneToMany', 'mediaSingle', 'mediaMultiple', 'computed', 'richText', 'json'].includes(field.type)
41
+ );
42
+
43
+ if (auditFields.length === 0) {
44
+ return false;
45
+ }
46
+
47
+ return auditFields.some(field => {
48
+ const fieldValue = entity[field.name];
49
+ return fieldValue !== undefined && fieldValue !== null;
50
+ });
51
+ }
52
+
53
+ async afterInsert(event: InsertEvent<any>) {
54
+ if (await this.shouldTrackAudit(event.entity, event.metadata)) {
55
+ const activeUser = this.userContextService.getUser();
56
+ await this.chatterMessageService.postAuditMessageOnInsert(event.entity, event.metadata, activeUser);
57
+ }
58
+ }
59
+
60
+ async afterUpdate(event: UpdateEvent<any>) {
61
+ if (await this.shouldTrackAudit(event.entity, event.metadata)) {
62
+ const activeUser = this.userContextService.getUser();
63
+ await this.chatterMessageService.postAuditMessageOnUpdate(event.entity, event.metadata, event.databaseEntity, activeUser);
64
+ }
65
+ }
66
+
67
+ async afterRemove(event: RemoveEvent<any>) {
68
+ if (await this.shouldTrackAudit(event.entity, event.metadata)) {
69
+ const activeUser = this.userContextService.getUser();
70
+ await this.chatterMessageService.postAuditMessageOnDelete(event.entity, event.metadata, event.databaseEntity, activeUser);
71
+ }
72
+ }
73
+ }