@solidstarters/solid-core 1.2.166 → 1.2.169
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/iam.config.d.ts +2 -0
- package/dist/config/iam.config.d.ts.map +1 -1
- package/dist/config/iam.config.js +1 -0
- package/dist/config/iam.config.js.map +1 -1
- package/dist/dtos/create-ai-interaction.dto.d.ts +3 -0
- package/dist/dtos/create-ai-interaction.dto.d.ts.map +1 -1
- package/dist/dtos/create-ai-interaction.dto.js +19 -1
- package/dist/dtos/create-ai-interaction.dto.js.map +1 -1
- package/dist/dtos/post-chatter-message.dto.js.map +1 -1
- package/dist/dtos/update-ai-interaction.dto.d.ts +3 -0
- package/dist/dtos/update-ai-interaction.dto.d.ts.map +1 -1
- package/dist/dtos/update-ai-interaction.dto.js +19 -1
- package/dist/dtos/update-ai-interaction.dto.js.map +1 -1
- package/dist/entities/ai-interaction.entity.d.ts +3 -0
- package/dist/entities/ai-interaction.entity.d.ts.map +1 -1
- package/dist/entities/ai-interaction.entity.js +13 -1
- package/dist/entities/ai-interaction.entity.js.map +1 -1
- package/dist/entities/chatter-message.entity.js.map +1 -1
- package/dist/filters/http-exception.filter.d.ts.map +1 -1
- package/dist/filters/http-exception.filter.js +2 -1
- package/dist/filters/http-exception.filter.js.map +1 -1
- package/dist/helpers/security.helper.d.ts +4 -2
- package/dist/helpers/security.helper.d.ts.map +1 -1
- package/dist/helpers/security.helper.js +38 -23
- package/dist/helpers/security.helper.js.map +1 -1
- package/dist/helpers/solid-core-error-codes-provider.service.js +3 -3
- package/dist/helpers/solid-core-error-codes-provider.service.js.map +1 -1
- package/dist/helpers/solid-registry.d.ts +0 -1
- package/dist/helpers/solid-registry.d.ts.map +1 -1
- package/dist/helpers/solid-registry.js +0 -1
- package/dist/helpers/solid-registry.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.js +2 -2
- package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +33 -0
- package/dist/services/authentication.service.d.ts +1 -5
- package/dist/services/authentication.service.d.ts.map +1 -1
- package/dist/services/authentication.service.js +22 -63
- package/dist/services/authentication.service.js.map +1 -1
- package/dist/services/chatter-message.service.d.ts.map +1 -1
- package/dist/services/chatter-message.service.js.map +1 -1
- package/dist/services/mcp-tool-response-handlers/solid-create-module-mcp-tool-response-handler.service.js +1 -1
- package/dist/services/mcp-tool-response-handlers/solid-create-module-mcp-tool-response-handler.service.js.map +1 -1
- package/dist/services/model-metadata.service.js +1 -1
- package/dist/services/model-metadata.service.js.map +1 -1
- package/dist/services/setting.service.d.ts.map +1 -1
- package/dist/services/setting.service.js +2 -1
- package/dist/services/setting.service.js.map +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +0 -2
- package/dist/solid-core.module.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/config/iam.config.ts +1 -0
- package/src/dtos/create-ai-interaction.dto.ts +15 -0
- package/src/dtos/post-chatter-message.dto.ts +1 -1
- package/src/dtos/update-ai-interaction.dto.ts +15 -0
- package/src/entities/ai-interaction.entity.ts +9 -0
- package/src/entities/chatter-message.entity.ts +3 -3
- package/src/filters/http-exception.filter.ts +5 -2
- package/src/helpers/security.helper.ts +95 -30
- package/src/helpers/solid-core-error-codes-provider.service.ts +4 -4
- package/src/helpers/solid-registry.ts +0 -1
- package/src/index.ts +0 -1
- package/src/jobs/database/trigger-mcp-client-subscriber-database.service.ts +2 -2
- package/src/seeders/seed-data/solid-core-metadata.json +35 -2
- package/src/services/authentication.service.ts +31 -142
- package/src/services/chatter-message.service.ts +373 -374
- package/src/services/mcp-tool-response-handlers/solid-create-module-mcp-tool-response-handler.service.ts +1 -1
- package/src/services/model-metadata.service.ts +1 -1
- package/src/services/setting.service.ts +2 -1
- package/src/solid-core.module.ts +0 -8
- package/dist/entities/user-password-history.entity.d.ts +0 -7
- package/dist/entities/user-password-history.entity.d.ts.map +0 -1
- package/dist/entities/user-password-history.entity.js +0 -35
- package/dist/entities/user-password-history.entity.js.map +0 -1
- package/src/entities/user-password-history.entity.ts +0 -14
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Injectable } from '@nestjs/common';
|
|
2
2
|
import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
|
|
3
|
-
import { DiscoveryService, ModuleRef
|
|
3
|
+
import { DiscoveryService, ModuleRef } from "@nestjs/core";
|
|
4
4
|
import { EntityManager, Repository, EntityMetadata } from 'typeorm';
|
|
5
5
|
|
|
6
6
|
import { CRUDService } from 'src/services/crud.service';
|
|
@@ -10,7 +10,6 @@ import { ConfigService } from '@nestjs/config';
|
|
|
10
10
|
import { FileService } from 'src/services/file.service';
|
|
11
11
|
import { CrudHelperService } from 'src/services/crud-helper.service';
|
|
12
12
|
import { PostChatterMessageDto } from 'src/dtos/post-chatter-message.dto';
|
|
13
|
-
import { SolidRequestContextDto } from 'src/dtos/solid-request-context.dto';
|
|
14
13
|
import { ChatterMessage } from '../entities/chatter-message.entity';
|
|
15
14
|
import { getMediaStorageProvider } from './mediaStorageProviders';
|
|
16
15
|
import { MediaStorageProviderType } from '../dtos/create-media-storage-provider-metadata.dto';
|
|
@@ -21,453 +20,453 @@ import { ChatterMessageRepository } from 'src/repository/chatter-message.reposit
|
|
|
21
20
|
import { lowerFirst } from 'src/helpers/string.helper';
|
|
22
21
|
import { ModelMetadataHelperService } from 'src/helpers/model-metadata-helper.service';
|
|
23
22
|
@Injectable()
|
|
24
|
-
export class ChatterMessageService extends CRUDService<ChatterMessage>{
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
23
|
+
export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
24
|
+
constructor(
|
|
25
|
+
readonly modelMetadataService: ModelMetadataService,
|
|
26
|
+
readonly moduleMetadataService: ModuleMetadataService,
|
|
27
|
+
readonly configService: ConfigService,
|
|
28
|
+
readonly fileService: FileService,
|
|
29
|
+
readonly discoveryService: DiscoveryService,
|
|
30
|
+
readonly crudHelperService: CrudHelperService,
|
|
31
|
+
@InjectEntityManager()
|
|
32
|
+
readonly entityManager: EntityManager,
|
|
33
|
+
// @InjectRepository(ChatterMessage, 'default')
|
|
34
|
+
readonly repo: ChatterMessageRepository,
|
|
35
|
+
@InjectRepository(ChatterMessageDetails, 'default')
|
|
36
|
+
readonly chatterMessageDetailsRepo: Repository<ChatterMessageDetails>,
|
|
37
|
+
readonly moduleRef: ModuleRef,
|
|
38
|
+
@InjectRepository(ModelMetadata)
|
|
39
|
+
private readonly modelMetadataRepo: Repository<ModelMetadata>,
|
|
40
|
+
readonly requestContextService: RequestContextService,
|
|
41
|
+
private readonly modelMetadataHelperService: ModelMetadataHelperService,
|
|
42
|
+
) {
|
|
43
|
+
super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService, entityManager, repo, 'chatterMessage', 'solid-core', moduleRef);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async postMessage(postDto: PostChatterMessageDto, files: Express.Multer.File[] = []) {
|
|
47
|
+
const chatterMessage = new ChatterMessage();
|
|
48
|
+
chatterMessage.messageType = 'custom';
|
|
49
|
+
chatterMessage.messageSubType = postDto.messageSubType || 'post_message';
|
|
50
|
+
chatterMessage.messageBody = postDto.messageBody;
|
|
51
|
+
chatterMessage.coModelEntityId = postDto.coModelEntityId;
|
|
52
|
+
chatterMessage.coModelName = postDto.coModelName;
|
|
53
|
+
|
|
54
|
+
const activeUser = this.requestContextService.getActiveUser();
|
|
55
|
+
|
|
56
|
+
if (activeUser) {
|
|
57
|
+
const userId = activeUser?.sub;
|
|
58
|
+
chatterMessage.user = { id: userId } as any;
|
|
59
|
+
} else {
|
|
60
|
+
chatterMessage.user = null;
|
|
61
|
+
}
|
|
63
62
|
|
|
64
|
-
|
|
63
|
+
const savedMessage = await this.repo.save(chatterMessage);
|
|
64
|
+
|
|
65
|
+
if (files && files.length > 0) {
|
|
66
|
+
const model = await this.modelMetadataService.findOneBySingularName('chatterMessage', {
|
|
67
|
+
fields: {
|
|
68
|
+
model: true,
|
|
69
|
+
mediaStorageProvider: true,
|
|
70
|
+
},
|
|
71
|
+
module: true,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const mediaFields = model.fields.filter(field => field.type === 'mediaSingle' || field.type === 'mediaMultiple');
|
|
75
|
+
|
|
76
|
+
for (const mediaField of mediaFields) {
|
|
77
|
+
const media = files.filter(multerFile => multerFile.fieldname === mediaField.name);
|
|
78
|
+
if (media.length > 0) {
|
|
79
|
+
const storageProviderMetadata = mediaField.mediaStorageProvider;
|
|
80
|
+
const storageProviderType = storageProviderMetadata.type as MediaStorageProviderType;
|
|
81
|
+
const storageProvider = await getMediaStorageProvider(this.moduleRef, storageProviderType);
|
|
82
|
+
await storageProvider.store(media, savedMessage, mediaField);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
65
86
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
fields: {
|
|
69
|
-
model: true,
|
|
70
|
-
mediaStorageProvider: true,
|
|
71
|
-
},
|
|
72
|
-
module: true,
|
|
73
|
-
});
|
|
87
|
+
return savedMessage;
|
|
88
|
+
}
|
|
74
89
|
|
|
75
|
-
|
|
90
|
+
async postAuditMessageOnInsert(entity: any, metadata: EntityMetadata, messageQueue: boolean = false) {
|
|
91
|
+
if (!entity) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const model = await this.modelMetadataRepo.findOne({
|
|
95
|
+
where: {
|
|
96
|
+
singularName: lowerFirst(metadata.name)
|
|
97
|
+
},
|
|
98
|
+
relations: {
|
|
99
|
+
fields: true,
|
|
100
|
+
module: true
|
|
101
|
+
}
|
|
102
|
+
});
|
|
76
103
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (media.length > 0) {
|
|
80
|
-
const storageProviderMetadata = mediaField.mediaStorageProvider;
|
|
81
|
-
const storageProviderType = storageProviderMetadata.type as MediaStorageProviderType;
|
|
82
|
-
const storageProvider = await getMediaStorageProvider(this.moduleRef, storageProviderType);
|
|
83
|
-
await storageProvider.store(media, savedMessage, mediaField);
|
|
104
|
+
if (!model || !model.enableAuditTracking) {
|
|
105
|
+
return;
|
|
84
106
|
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
107
|
|
|
88
|
-
|
|
89
|
-
|
|
108
|
+
const auditFields = model.fields.filter(field =>
|
|
109
|
+
field.enableAuditTracking &&
|
|
110
|
+
!['mediaSingle', 'mediaMultiple', 'computed', 'richText', 'json'].includes(field.type) &&
|
|
111
|
+
!(field.type === 'relation' && field.relationType === 'one-to-many')
|
|
112
|
+
);
|
|
90
113
|
|
|
91
|
-
|
|
92
|
-
if(!entity){
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
const model = await this.modelMetadataRepo.findOne({
|
|
96
|
-
where: {
|
|
97
|
-
singularName: lowerFirst(metadata.name)
|
|
98
|
-
},
|
|
99
|
-
relations: {
|
|
100
|
-
fields: true,
|
|
101
|
-
module: true
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
if (!model || !model.enableAuditTracking) {
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
114
|
+
const activeUser = this.requestContextService.getActiveUser();
|
|
108
115
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const activeUser = this.requestContextService.getActiveUser();
|
|
116
|
-
|
|
117
|
-
const chatterMessage = new ChatterMessage();
|
|
118
|
-
chatterMessage.messageType = 'audit';
|
|
119
|
-
chatterMessage.messageSubType = 'insert';
|
|
120
|
-
chatterMessage.coModelEntityId = entity.id;
|
|
121
|
-
chatterMessage.coModelName = model.singularName;
|
|
122
|
-
chatterMessage.messageBody = `New ${model.displayName} created`;
|
|
123
|
-
|
|
124
|
-
if (activeUser) {
|
|
125
|
-
const userId = activeUser?.sub;
|
|
126
|
-
chatterMessage.user = { id: userId } as any;
|
|
127
|
-
} else {
|
|
128
|
-
chatterMessage.user = null;
|
|
129
|
-
}
|
|
116
|
+
const chatterMessage = new ChatterMessage();
|
|
117
|
+
chatterMessage.messageType = 'audit';
|
|
118
|
+
chatterMessage.messageSubType = 'insert';
|
|
119
|
+
chatterMessage.coModelEntityId = entity.id;
|
|
120
|
+
chatterMessage.coModelName = model.singularName;
|
|
121
|
+
chatterMessage.messageBody = `New ${model.displayName} created`;
|
|
130
122
|
|
|
131
|
-
|
|
123
|
+
if (activeUser) {
|
|
124
|
+
const userId = activeUser?.sub;
|
|
125
|
+
chatterMessage.user = { id: userId } as any;
|
|
126
|
+
} else {
|
|
127
|
+
chatterMessage.user = null;
|
|
128
|
+
}
|
|
132
129
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
130
|
+
const savedMessage = await this.repo.save(chatterMessage);
|
|
131
|
+
|
|
132
|
+
for (const field of auditFields) {
|
|
133
|
+
const fieldValue = entity[field.name];
|
|
134
|
+
if (fieldValue !== undefined && fieldValue !== null && fieldValue !== '') {
|
|
135
|
+
const messageDetail = new ChatterMessageDetails();
|
|
136
|
+
messageDetail.chatterMessage = savedMessage;
|
|
137
|
+
messageDetail.fieldName = field.name;
|
|
138
|
+
messageDetail.oldValue = null;
|
|
139
|
+
messageDetail.oldValueDisplay = null;
|
|
140
|
+
messageDetail.newValue = this.formatFieldValue(field, fieldValue);
|
|
141
|
+
messageDetail.newValueDisplay = this.formatFieldValueDisplay(field, fieldValue);
|
|
142
|
+
await this.chatterMessageDetailsRepo.save(messageDetail);
|
|
143
|
+
}
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
|
-
}
|
|
147
146
|
|
|
148
|
-
async postAuditMessageOnUpdate(entity: any, metadata: EntityMetadata, databaseEntity: any, updatedColumns: any[] = [], messageQueue: boolean = false) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
);
|
|
173
|
-
|
|
174
|
-
const updatedFieldNames = new Set(updatedColumns.map(col => col.propertyName));
|
|
175
|
-
|
|
176
|
-
const allNonRelationFields = auditFields.filter(field => field.type !== 'relation');
|
|
177
|
-
const allRelationFields = auditFields.filter(field => field.type === 'relation');
|
|
178
|
-
|
|
179
|
-
let potentialNonRelationFields = [];
|
|
180
|
-
|
|
181
|
-
if (updatedColumns.length > 0) {
|
|
182
|
-
potentialNonRelationFields = allNonRelationFields.filter(field =>
|
|
183
|
-
updatedFieldNames.has(field.name)
|
|
147
|
+
async postAuditMessageOnUpdate(entity: any, metadata: EntityMetadata, databaseEntity: any, updatedColumns: any[] = [], messageQueue: boolean = false) {
|
|
148
|
+
if (!databaseEntity || !entity) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const model = await this.modelMetadataRepo.findOne({
|
|
152
|
+
where: {
|
|
153
|
+
singularName: lowerFirst(metadata.name)
|
|
154
|
+
},
|
|
155
|
+
relations: {
|
|
156
|
+
fields: true,
|
|
157
|
+
module: true
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
if (!model || !model.enableAuditTracking) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const modelFields = await this.modelMetadataHelperService.loadFieldHierarchy(model.singularName)
|
|
166
|
+
|
|
167
|
+
const auditFields = modelFields.filter(field =>
|
|
168
|
+
field.enableAuditTracking &&
|
|
169
|
+
!['mediaSingle', 'mediaMultiple', 'computed', 'richText', 'json'].includes(field.type) &&
|
|
170
|
+
!(field.type === 'relation' && field.relationType === 'one-to-many')
|
|
184
171
|
);
|
|
185
|
-
} else {
|
|
186
|
-
potentialNonRelationFields = allNonRelationFields;
|
|
187
|
-
}
|
|
188
172
|
|
|
189
|
-
|
|
173
|
+
const updatedFieldNames = new Set(updatedColumns.map(col => col.propertyName));
|
|
190
174
|
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
const oldValue = databaseEntity[field.name];
|
|
194
|
-
return this.hasValueChanged(newValue, oldValue);
|
|
195
|
-
});
|
|
175
|
+
const allNonRelationFields = auditFields.filter(field => field.type !== 'relation');
|
|
176
|
+
const allRelationFields = auditFields.filter(field => field.type === 'relation');
|
|
196
177
|
|
|
197
|
-
|
|
198
|
-
if (potentialRelationFields.length > 0) {
|
|
199
|
-
const populatedOldEntity = await this.populateRelationFields(databaseEntity, potentialRelationFields, metadata);
|
|
178
|
+
let potentialNonRelationFields = [];
|
|
200
179
|
|
|
201
|
-
|
|
180
|
+
if (updatedColumns.length > 0) {
|
|
181
|
+
potentialNonRelationFields = allNonRelationFields.filter(field =>
|
|
182
|
+
updatedFieldNames.has(field.name)
|
|
183
|
+
);
|
|
184
|
+
} else {
|
|
185
|
+
potentialNonRelationFields = allNonRelationFields;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const potentialRelationFields = allRelationFields;
|
|
189
|
+
|
|
190
|
+
const changedNonRelationFields = potentialNonRelationFields.filter(field => {
|
|
202
191
|
const newValue = entity[field.name];
|
|
203
|
-
const oldValue =
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
192
|
+
const oldValue = databaseEntity[field.name];
|
|
193
|
+
return this.hasValueChanged(newValue, oldValue);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const changedRelationFields = [];
|
|
197
|
+
if (potentialRelationFields.length > 0) {
|
|
198
|
+
const populatedOldEntity = await this.populateRelationFields(databaseEntity, potentialRelationFields, metadata);
|
|
199
|
+
|
|
200
|
+
for (const field of potentialRelationFields) {
|
|
201
|
+
const newValue = entity[field.name];
|
|
202
|
+
const oldValue = populatedOldEntity[field.name];
|
|
203
|
+
|
|
204
|
+
if (this.hasRelationValueChanged(field, newValue, oldValue)) {
|
|
205
|
+
changedRelationFields.push({
|
|
206
|
+
field,
|
|
207
|
+
newValue,
|
|
208
|
+
oldValue
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
211
|
}
|
|
212
212
|
}
|
|
213
|
-
}
|
|
214
213
|
|
|
215
214
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
if (allChangedFields.length === 0) {
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
215
|
+
const allChangedFields = [
|
|
216
|
+
...changedNonRelationFields.map(field => ({
|
|
217
|
+
field,
|
|
218
|
+
newValue: entity[field.name],
|
|
219
|
+
oldValue: databaseEntity[field.name]
|
|
220
|
+
})),
|
|
221
|
+
...changedRelationFields
|
|
222
|
+
];
|
|
228
223
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
chatterMessage.messageType = 'audit';
|
|
233
|
-
chatterMessage.messageSubType = 'update';
|
|
234
|
-
chatterMessage.coModelEntityId = entity.id;
|
|
235
|
-
chatterMessage.coModelName = model.singularName;
|
|
236
|
-
chatterMessage.messageBody = `${model.displayName} updated`;
|
|
237
|
-
|
|
238
|
-
if (activeUser) {
|
|
239
|
-
const userId = activeUser?.sub;
|
|
240
|
-
chatterMessage.user = { id: userId } as any;
|
|
241
|
-
} else {
|
|
242
|
-
chatterMessage.user = null;
|
|
243
|
-
}
|
|
224
|
+
if (allChangedFields.length === 0) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
244
227
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
for (const { field, newValue, oldValue } of allChangedFields) {
|
|
248
|
-
const messageDetail = new ChatterMessageDetails();
|
|
249
|
-
messageDetail.chatterMessage = savedMessage;
|
|
250
|
-
messageDetail.fieldName = field.name;
|
|
251
|
-
messageDetail.oldValue = this.formatFieldValue(field, oldValue);
|
|
252
|
-
messageDetail.newValue = this.formatFieldValue(field, newValue);
|
|
253
|
-
messageDetail.oldValueDisplay = this.formatFieldValueDisplay(field, oldValue);
|
|
254
|
-
messageDetail.newValueDisplay = this.formatFieldValueDisplay(field, newValue);
|
|
255
|
-
await this.chatterMessageDetailsRepo.save(messageDetail);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
228
|
+
const activeUser = this.requestContextService.getActiveUser();
|
|
258
229
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
230
|
+
const chatterMessage = new ChatterMessage();
|
|
231
|
+
chatterMessage.messageType = 'audit';
|
|
232
|
+
chatterMessage.messageSubType = 'update';
|
|
233
|
+
chatterMessage.coModelEntityId = entity.id;
|
|
234
|
+
chatterMessage.coModelName = model.singularName;
|
|
235
|
+
chatterMessage.messageBody = `${model.displayName} updated`;
|
|
236
|
+
|
|
237
|
+
if (activeUser) {
|
|
238
|
+
const userId = activeUser?.sub;
|
|
239
|
+
chatterMessage.user = { id: userId } as any;
|
|
240
|
+
} else {
|
|
241
|
+
chatterMessage.user = null;
|
|
267
242
|
}
|
|
268
|
-
});
|
|
269
243
|
|
|
270
|
-
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
244
|
+
const savedMessage = await this.repo.save(chatterMessage);
|
|
273
245
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const userId = activeUser?.sub;
|
|
285
|
-
chatterMessage.user = { id: userId } as any;
|
|
286
|
-
} else {
|
|
287
|
-
chatterMessage.user = null;
|
|
246
|
+
for (const { field, newValue, oldValue } of allChangedFields) {
|
|
247
|
+
const messageDetail = new ChatterMessageDetails();
|
|
248
|
+
messageDetail.chatterMessage = savedMessage;
|
|
249
|
+
messageDetail.fieldName = field.name;
|
|
250
|
+
messageDetail.oldValue = this.formatFieldValue(field, oldValue);
|
|
251
|
+
messageDetail.newValue = this.formatFieldValue(field, newValue);
|
|
252
|
+
messageDetail.oldValueDisplay = this.formatFieldValueDisplay(field, oldValue);
|
|
253
|
+
messageDetail.newValueDisplay = this.formatFieldValueDisplay(field, newValue);
|
|
254
|
+
await this.chatterMessageDetailsRepo.save(messageDetail);
|
|
255
|
+
}
|
|
288
256
|
}
|
|
289
257
|
|
|
290
|
-
|
|
291
|
-
|
|
258
|
+
async postAuditMessageOnDelete(entity: any, metadata: EntityMetadata, databaseEntity: any, messageQueue: boolean = false) {
|
|
259
|
+
const model = await this.modelMetadataRepo.findOne({
|
|
260
|
+
where: {
|
|
261
|
+
singularName: lowerFirst(metadata.name)
|
|
262
|
+
},
|
|
263
|
+
relations: {
|
|
264
|
+
fields: true,
|
|
265
|
+
module: true
|
|
266
|
+
}
|
|
267
|
+
});
|
|
292
268
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
269
|
+
if (!model || !model.enableAuditTracking) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const chatterMessage = new ChatterMessage();
|
|
274
|
+
chatterMessage.messageType = 'audit';
|
|
275
|
+
chatterMessage.messageSubType = 'delete';
|
|
276
|
+
chatterMessage.coModelEntityId = databaseEntity.id;
|
|
277
|
+
chatterMessage.coModelName = model.singularName;
|
|
278
|
+
chatterMessage.messageBody = `${model.displayName} deleted`;
|
|
279
|
+
|
|
280
|
+
const activeUser = this.requestContextService.getActiveUser();
|
|
297
281
|
|
|
298
|
-
|
|
299
|
-
|
|
282
|
+
if (activeUser) {
|
|
283
|
+
const userId = activeUser?.sub;
|
|
284
|
+
chatterMessage.user = { id: userId } as any;
|
|
285
|
+
} else {
|
|
286
|
+
chatterMessage.user = null;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
await this.repo.save(chatterMessage);
|
|
300
290
|
}
|
|
301
291
|
|
|
302
|
-
|
|
303
|
-
if (
|
|
304
|
-
return
|
|
292
|
+
private formatFieldValue(field: any, value: any): string {
|
|
293
|
+
if (value === null || value === undefined) {
|
|
294
|
+
return '';
|
|
305
295
|
}
|
|
306
|
-
|
|
307
|
-
|
|
296
|
+
|
|
297
|
+
if (field.type === 'selectionStatic' || field.type === 'selectionDynamic') {
|
|
298
|
+
return `${value}`;
|
|
308
299
|
}
|
|
309
|
-
}
|
|
310
300
|
|
|
301
|
+
if (field.type === 'relation') {
|
|
302
|
+
if (field.relationType === "many-to-one") {
|
|
303
|
+
return value.id;
|
|
304
|
+
}
|
|
305
|
+
if (field.relationType === 'manyToMany') {
|
|
306
|
+
return value.map(item => item.id).join(', ');
|
|
307
|
+
}
|
|
308
|
+
}
|
|
311
309
|
|
|
312
|
-
return value.toString();
|
|
313
|
-
}
|
|
314
310
|
|
|
315
|
-
|
|
316
|
-
if (value === null || value === undefined) {
|
|
317
|
-
return '';
|
|
311
|
+
return value.toString();
|
|
318
312
|
}
|
|
319
313
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
314
|
+
private formatFieldValueDisplay(field: any, value: any): string {
|
|
315
|
+
if (value === null || value === undefined) {
|
|
316
|
+
return '';
|
|
317
|
+
}
|
|
323
318
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
return value.name;
|
|
319
|
+
if (field.type === 'selectionStatic' || field.type === 'selectionDynamic') {
|
|
320
|
+
return `${value}`;
|
|
327
321
|
}
|
|
328
|
-
|
|
329
|
-
|
|
322
|
+
|
|
323
|
+
if (field.type === 'relation') {
|
|
324
|
+
if (field.relationType === "many-to-one") {
|
|
325
|
+
return value.name;
|
|
326
|
+
}
|
|
327
|
+
if (field.relationType === 'many-toMany') {
|
|
328
|
+
return value.map(item => item.name).join(', ');
|
|
329
|
+
}
|
|
330
330
|
}
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
return value.toString();
|
|
331
334
|
}
|
|
332
335
|
|
|
336
|
+
private hasValueChanged(newValue: any, oldValue: any): boolean {
|
|
337
|
+
if (newValue === oldValue) {
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
333
340
|
|
|
334
|
-
|
|
335
|
-
|
|
341
|
+
if (newValue === null && oldValue === null) {
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
336
344
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}
|
|
345
|
+
if (newValue === undefined && oldValue === undefined) {
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
341
348
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
349
|
+
if (newValue && oldValue && typeof newValue === 'object' && typeof oldValue === 'object') {
|
|
350
|
+
if (newValue.id !== undefined && oldValue.id !== undefined) {
|
|
351
|
+
return newValue.id !== oldValue.id;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (Array.isArray(newValue) && Array.isArray(oldValue)) {
|
|
355
|
+
if (newValue.length !== oldValue.length) {
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
const newIds = newValue.map(item => item.id || item).sort();
|
|
359
|
+
const oldIds = oldValue.map(item => item.id || item).sort();
|
|
360
|
+
return JSON.stringify(newIds) !== JSON.stringify(oldIds);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
345
363
|
|
|
346
|
-
|
|
347
|
-
|
|
364
|
+
if (Array.isArray(newValue) && Array.isArray(oldValue)) {
|
|
365
|
+
return JSON.stringify(newValue) !== JSON.stringify(oldValue);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return true;
|
|
348
369
|
}
|
|
349
370
|
|
|
350
|
-
|
|
351
|
-
if (newValue
|
|
352
|
-
return
|
|
371
|
+
private hasRelationValueChanged(field: any, newValue: any, oldValue: any): boolean {
|
|
372
|
+
if (newValue === oldValue) {
|
|
373
|
+
return false;
|
|
353
374
|
}
|
|
354
|
-
|
|
355
|
-
if (
|
|
356
|
-
|
|
375
|
+
|
|
376
|
+
if ((newValue === null || newValue === undefined) && (oldValue === null || oldValue === undefined)) {
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (field.relationType === 'many-to-one') {
|
|
381
|
+
const newId = this.extractRelationId(newValue);
|
|
382
|
+
const oldId = this.extractRelationId(oldValue);
|
|
383
|
+
return newId !== oldId;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (field.relationType === 'many-to-many' || field.relationType === 'manyToMany') {
|
|
387
|
+
const newIds = this.extractRelationIds(newValue);
|
|
388
|
+
const oldIds = this.extractRelationIds(oldValue);
|
|
389
|
+
|
|
390
|
+
if (newIds.length !== oldIds.length) {
|
|
357
391
|
return true;
|
|
358
392
|
}
|
|
359
|
-
|
|
360
|
-
|
|
393
|
+
|
|
394
|
+
newIds.sort();
|
|
395
|
+
oldIds.sort();
|
|
396
|
+
|
|
361
397
|
return JSON.stringify(newIds) !== JSON.stringify(oldIds);
|
|
362
398
|
}
|
|
363
|
-
}
|
|
364
399
|
|
|
365
|
-
|
|
366
|
-
return JSON.stringify(newValue) !== JSON.stringify(oldValue);
|
|
400
|
+
return this.hasValueChanged(newValue, oldValue);
|
|
367
401
|
}
|
|
368
402
|
|
|
369
|
-
|
|
370
|
-
|
|
403
|
+
private extractRelationId(value: any): any {
|
|
404
|
+
if (value === null || value === undefined) {
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
371
407
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
if ((newValue === null || newValue === undefined) && (oldValue === null || oldValue === undefined)) {
|
|
378
|
-
return false;
|
|
379
|
-
}
|
|
408
|
+
if (typeof value === 'string' || typeof value === 'number') {
|
|
409
|
+
return value;
|
|
410
|
+
}
|
|
380
411
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
return newId !== oldId;
|
|
385
|
-
}
|
|
412
|
+
if (typeof value === 'object' && value.id !== undefined) {
|
|
413
|
+
return value.id;
|
|
414
|
+
}
|
|
386
415
|
|
|
387
|
-
|
|
388
|
-
const newIds = this.extractRelationIds(newValue);
|
|
389
|
-
const oldIds = this.extractRelationIds(oldValue);
|
|
390
|
-
|
|
391
|
-
if (newIds.length !== oldIds.length) {
|
|
392
|
-
return true;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
newIds.sort();
|
|
396
|
-
oldIds.sort();
|
|
397
|
-
|
|
398
|
-
return JSON.stringify(newIds) !== JSON.stringify(oldIds);
|
|
416
|
+
return null;
|
|
399
417
|
}
|
|
400
418
|
|
|
401
|
-
|
|
402
|
-
|
|
419
|
+
private extractRelationIds(value: any): any[] {
|
|
420
|
+
if (!Array.isArray(value)) {
|
|
421
|
+
const id = this.extractRelationId(value);
|
|
422
|
+
return id !== null ? [id] : [];
|
|
423
|
+
}
|
|
403
424
|
|
|
404
|
-
|
|
405
|
-
if (value === null || value === undefined) {
|
|
406
|
-
return null;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
if (typeof value === 'string' || typeof value === 'number') {
|
|
410
|
-
return value;
|
|
425
|
+
return value.map(item => this.extractRelationId(item)).filter(id => id !== null);
|
|
411
426
|
}
|
|
412
|
-
|
|
413
|
-
if (typeof value === 'object' && value.id !== undefined) {
|
|
414
|
-
return value.id;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
return null;
|
|
418
|
-
}
|
|
419
427
|
|
|
420
|
-
private
|
|
421
|
-
|
|
422
|
-
const id = this.extractRelationId(value);
|
|
423
|
-
return id !== null ? [id] : [];
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
return value.map(item => this.extractRelationId(item)).filter(id => id !== null);
|
|
427
|
-
}
|
|
428
|
+
private async populateRelationFields(databaseEntity: any, relationFields: any[], metadata: EntityMetadata): Promise<any> {
|
|
429
|
+
const populatedEntity = { ...databaseEntity };
|
|
428
430
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
for (const field of relationFields) {
|
|
433
|
-
const relationValue = databaseEntity[field.name];
|
|
434
|
-
|
|
435
|
-
if (relationValue === null || relationValue === undefined) {
|
|
436
|
-
populatedEntity[field.name] = relationValue;
|
|
437
|
-
continue;
|
|
438
|
-
}
|
|
431
|
+
for (const field of relationFields) {
|
|
432
|
+
const relationValue = databaseEntity[field.name];
|
|
439
433
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
}
|
|
434
|
+
if (relationValue === null || relationValue === undefined) {
|
|
435
|
+
populatedEntity[field.name] = relationValue;
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
445
438
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
if (relationId) {
|
|
451
|
-
const relatedEntity = await this.entityManager.findOne(targetEntity as any, {
|
|
452
|
-
where: { id: relationId }
|
|
453
|
-
});
|
|
454
|
-
populatedEntity[field.name] = relatedEntity;
|
|
455
|
-
} else {
|
|
456
|
-
populatedEntity[field.name] = relationValue;
|
|
439
|
+
const relationMetadata = metadata.relations.find(rel => rel.propertyName === field.name);
|
|
440
|
+
if (!relationMetadata) {
|
|
441
|
+
populatedEntity[field.name] = relationValue;
|
|
442
|
+
continue;
|
|
457
443
|
}
|
|
458
|
-
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
444
|
+
|
|
445
|
+
const targetEntity = relationMetadata.inverseEntityMetadata || relationMetadata.type;
|
|
446
|
+
|
|
447
|
+
if (field.relationType === 'many-to-one') {
|
|
448
|
+
const relationId = this.extractRelationId(relationValue);
|
|
449
|
+
if (relationId) {
|
|
450
|
+
const relatedEntity = await this.entityManager.findOne(targetEntity as any, {
|
|
451
|
+
where: { id: relationId }
|
|
452
|
+
});
|
|
453
|
+
populatedEntity[field.name] = relatedEntity;
|
|
454
|
+
} else {
|
|
455
|
+
populatedEntity[field.name] = relationValue;
|
|
456
|
+
}
|
|
457
|
+
} else if (field.relationType === 'many-to-many' || field.relationType === 'manyToMany') {
|
|
458
|
+
const relationIds = this.extractRelationIds(relationValue);
|
|
459
|
+
if (relationIds.length > 0) {
|
|
460
|
+
const relatedEntities = await this.entityManager.findByIds(targetEntity as any, relationIds);
|
|
461
|
+
populatedEntity[field.name] = relatedEntities;
|
|
462
|
+
} else {
|
|
463
|
+
populatedEntity[field.name] = relationValue;
|
|
464
|
+
}
|
|
463
465
|
} else {
|
|
464
|
-
populatedEntity[field.name] = relationValue;
|
|
466
|
+
populatedEntity[field.name] = relationValue;
|
|
465
467
|
}
|
|
466
|
-
} else {
|
|
467
|
-
populatedEntity[field.name] = relationValue;
|
|
468
468
|
}
|
|
469
|
+
|
|
470
|
+
return populatedEntity;
|
|
469
471
|
}
|
|
470
|
-
|
|
471
|
-
return populatedEntity;
|
|
472
|
-
}
|
|
473
472
|
}
|