@solidstarters/solid-core 1.2.173 → 1.2.175

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 (88) hide show
  1. package/dev-grooming-docs/ozzy-prompts.txt +13 -0
  2. package/dist/config/iam.config.js +2 -2
  3. package/dist/config/iam.config.js.map +1 -1
  4. package/dist/constants/chatter-message.constants.d.ts +11 -0
  5. package/dist/constants/chatter-message.constants.d.ts.map +1 -0
  6. package/dist/constants/chatter-message.constants.js +14 -0
  7. package/dist/constants/chatter-message.constants.js.map +1 -0
  8. package/dist/controllers/ai-interaction.controller.js +1 -1
  9. package/dist/controllers/ai-interaction.controller.js.map +1 -1
  10. package/dist/controllers/authentication.controller.d.ts +1 -2
  11. package/dist/controllers/authentication.controller.d.ts.map +1 -1
  12. package/dist/controllers/chatter-message-details.controller.d.ts +0 -16
  13. package/dist/controllers/chatter-message-details.controller.d.ts.map +1 -1
  14. package/dist/controllers/chatter-message-details.controller.js +0 -109
  15. package/dist/controllers/chatter-message-details.controller.js.map +1 -1
  16. package/dist/controllers/chatter-message.controller.d.ts +10 -13
  17. package/dist/controllers/chatter-message.controller.d.ts.map +1 -1
  18. package/dist/controllers/chatter-message.controller.js +19 -88
  19. package/dist/controllers/chatter-message.controller.js.map +1 -1
  20. package/dist/controllers/service.controller.js +2 -1
  21. package/dist/controllers/service.controller.js.map +1 -1
  22. package/dist/dtos/invoke-ai-prompt.dto.d.ts +1 -0
  23. package/dist/dtos/invoke-ai-prompt.dto.d.ts.map +1 -1
  24. package/dist/dtos/invoke-ai-prompt.dto.js +6 -1
  25. package/dist/dtos/invoke-ai-prompt.dto.js.map +1 -1
  26. package/dist/entities/chatter-message-details.entity.d.ts +1 -0
  27. package/dist/entities/chatter-message-details.entity.d.ts.map +1 -1
  28. package/dist/entities/chatter-message-details.entity.js +5 -1
  29. package/dist/entities/chatter-message-details.entity.js.map +1 -1
  30. package/dist/entities/chatter-message.entity.d.ts +4 -0
  31. package/dist/entities/chatter-message.entity.d.ts.map +1 -1
  32. package/dist/entities/chatter-message.entity.js +14 -1
  33. package/dist/entities/chatter-message.entity.js.map +1 -1
  34. package/dist/entities/user.entity.d.ts +1 -0
  35. package/dist/entities/user.entity.d.ts.map +1 -1
  36. package/dist/entities/user.entity.js +5 -1
  37. package/dist/entities/user.entity.js.map +1 -1
  38. package/dist/interfaces.d.ts +2 -1
  39. package/dist/interfaces.d.ts.map +1 -1
  40. package/dist/interfaces.js.map +1 -1
  41. package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.d.ts +3 -2
  42. package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.d.ts.map +1 -1
  43. package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.js +61 -22
  44. package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.js.map +1 -1
  45. package/dist/repository/chatter-message-details.repository.d.ts.map +1 -1
  46. package/dist/repository/chatter-message-details.repository.js +7 -3
  47. package/dist/repository/chatter-message-details.repository.js.map +1 -1
  48. package/dist/repository/chatter-message.repository.d.ts.map +1 -1
  49. package/dist/repository/chatter-message.repository.js +4 -0
  50. package/dist/repository/chatter-message.repository.js.map +1 -1
  51. package/dist/seeders/seed-data/solid-core-metadata.json +78 -5
  52. package/dist/services/ai-interaction.service.d.ts +2 -1
  53. package/dist/services/ai-interaction.service.d.ts.map +1 -1
  54. package/dist/services/ai-interaction.service.js +15 -3
  55. package/dist/services/ai-interaction.service.js.map +1 -1
  56. package/dist/services/authentication.service.d.ts +2 -3
  57. package/dist/services/authentication.service.d.ts.map +1 -1
  58. package/dist/services/authentication.service.js +4 -4
  59. package/dist/services/authentication.service.js.map +1 -1
  60. package/dist/services/chatter-message.service.d.ts +16 -3
  61. package/dist/services/chatter-message.service.d.ts.map +1 -1
  62. package/dist/services/chatter-message.service.js +120 -25
  63. package/dist/services/chatter-message.service.js.map +1 -1
  64. package/dist/services/refresh-token-ids-storage.service.d.ts +2 -1
  65. package/dist/services/refresh-token-ids-storage.service.d.ts.map +1 -1
  66. package/dist/services/refresh-token-ids-storage.service.js +6 -7
  67. package/dist/services/refresh-token-ids-storage.service.js.map +1 -1
  68. package/dist/tsconfig.tsbuildinfo +1 -1
  69. package/package.json +1 -1
  70. package/src/config/iam.config.ts +2 -2
  71. package/src/constants/chatter-message.constants.ts +11 -0
  72. package/src/controllers/ai-interaction.controller.ts +1 -1
  73. package/src/controllers/chatter-message-details.controller.ts +44 -44
  74. package/src/controllers/chatter-message.controller.ts +58 -38
  75. package/src/controllers/service.controller.ts +2 -2
  76. package/src/dtos/invoke-ai-prompt.dto.ts +5 -1
  77. package/src/entities/chatter-message-details.entity.ts +3 -0
  78. package/src/entities/chatter-message.entity.ts +9 -2
  79. package/src/entities/user.entity.ts +2 -0
  80. package/src/interfaces.ts +2 -1
  81. package/src/jobs/database/trigger-mcp-client-subscriber-database.service.ts +108 -25
  82. package/src/repository/chatter-message-details.repository.ts +6 -5
  83. package/src/repository/chatter-message.repository.ts +2 -0
  84. package/src/seeders/seed-data/solid-core-metadata.json +78 -5
  85. package/src/services/ai-interaction.service.ts +24 -3
  86. package/src/services/authentication.service.ts +9 -4
  87. package/src/services/chatter-message.service.ts +143 -24
  88. package/src/services/refresh-token-ids-storage.service.ts +11 -7
@@ -3748,7 +3748,8 @@
3748
3748
  "selectionStaticValues": [
3749
3749
  "audit_insert:audit_insert",
3750
3750
  "audit_update:audit_update",
3751
- "audit_delete:audit_delete"
3751
+ "audit_delete:audit_delete",
3752
+ "custom:custom"
3752
3753
  ]
3753
3754
  },
3754
3755
  {
@@ -3835,6 +3836,30 @@
3835
3836
  "columnName": null,
3836
3837
  "relationJoinTableName": "",
3837
3838
  "isRelationManyToManyOwner": null
3839
+ },
3840
+ {
3841
+ "name": "modelDisplayName",
3842
+ "displayName": "Model Display Name",
3843
+ "type": "shortText",
3844
+ "ormType": "text",
3845
+ "required": false,
3846
+ "unique": false,
3847
+ "index": false,
3848
+ "private": false,
3849
+ "encrypt": false,
3850
+ "isSystem": false
3851
+ },
3852
+ {
3853
+ "name": "modelUserKey",
3854
+ "displayName": "Model User Key",
3855
+ "type": "shortText",
3856
+ "ormType": "text",
3857
+ "required": false,
3858
+ "unique": false,
3859
+ "index": false,
3860
+ "private": false,
3861
+ "encrypt": false,
3862
+ "isSystem": false
3838
3863
  }
3839
3864
  ]
3840
3865
  },
@@ -3910,6 +3935,19 @@
3910
3935
  "encrypt": false,
3911
3936
  "isSystem": false
3912
3937
  },
3938
+ {
3939
+ "name": "fieldDisplayName",
3940
+ "displayName": "Field Display Name",
3941
+ "type": "shortText",
3942
+ "ormType": "varchar",
3943
+ "length": 512,
3944
+ "required": false,
3945
+ "unique": false,
3946
+ "index": false,
3947
+ "private": false,
3948
+ "encrypt": false,
3949
+ "isSystem": false
3950
+ },
3913
3951
  {
3914
3952
  "name": "chatterMessage",
3915
3953
  "displayName": "Chatter Message",
@@ -12059,7 +12097,7 @@
12059
12097
  {
12060
12098
  "type": "field",
12061
12099
  "attrs": {
12062
- "name": "modelUsed"
12100
+ "name": "contentType"
12063
12101
  }
12064
12102
  },
12065
12103
  {
@@ -12067,6 +12105,24 @@
12067
12105
  "attrs": {
12068
12106
  "name": "responseTimeMs"
12069
12107
  }
12108
+ },
12109
+ {
12110
+ "type": "field",
12111
+ "attrs": {
12112
+ "name": "inputTokens"
12113
+ }
12114
+ },
12115
+ {
12116
+ "type": "field",
12117
+ "attrs": {
12118
+ "name": "outputTokens"
12119
+ }
12120
+ },
12121
+ {
12122
+ "type": "field",
12123
+ "attrs": {
12124
+ "name": "totalTokens"
12125
+ }
12070
12126
  }
12071
12127
  ]
12072
12128
  }
@@ -12184,6 +12240,24 @@
12184
12240
  "attrs": {
12185
12241
  "name": "parentInteraction"
12186
12242
  }
12243
+ },
12244
+ {
12245
+ "type": "field",
12246
+ "attrs": {
12247
+ "name": "inputTokens"
12248
+ }
12249
+ },
12250
+ {
12251
+ "type": "field",
12252
+ "attrs": {
12253
+ "name": "outputTokens"
12254
+ }
12255
+ },
12256
+ {
12257
+ "type": "field",
12258
+ "attrs": {
12259
+ "name": "totalTokens"
12260
+ }
12187
12261
  }
12188
12262
  ]
12189
12263
  }
@@ -12249,7 +12323,7 @@
12249
12323
  "type": "column",
12250
12324
  "attrs": {
12251
12325
  "name": "col-meta",
12252
- "label": "Metadata",
12326
+ "label": "",
12253
12327
  "className": "col-12"
12254
12328
  },
12255
12329
  "children": [
@@ -12257,8 +12331,7 @@
12257
12331
  "type": "field",
12258
12332
  "attrs": {
12259
12333
  "name": "metadata",
12260
- "editWidget": "codeEditor",
12261
- "editorLanguage": "json"
12334
+ "height": "80vh"
12262
12335
  }
12263
12336
  }
12264
12337
  ]
@@ -18,6 +18,7 @@ import { RequestContextService } from './request-context.service';
18
18
  import { ActiveUserData } from 'src/interfaces/active-user-data.interface';
19
19
  import { McpToolResponseHandlerFactory } from './mcp-tool-response-handlers/mcp-tool-response-handler-factory.service';
20
20
  import { ERROR_MESSAGES } from 'src/constants/error-messages';
21
+ import { InvokeAiPromptDto } from 'src/dtos/invoke-ai-prompt.dto';
21
22
 
22
23
  @Injectable()
23
24
  export class AiInteractionService extends CRUDService<AiInteraction> {
@@ -43,14 +44,14 @@ export class AiInteractionService extends CRUDService<AiInteraction> {
43
44
  super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService, entityManager, repo, 'aiInteraction', 'solid-core', moduleRef);
44
45
  }
45
46
 
46
- async triggerMcpClientJob(prompt: string, userId: number, isAutoApply: boolean = false, threadId: string = null): Promise<any> {
47
+ async triggerMcpClientJob(dto: InvokeAiPromptDto, userId: number, isAutoApply: boolean = false, threadId: string = null): Promise<any> {
47
48
  // const activeUser: ActiveUserData = this.requestContextService.getActiveUser();
48
49
 
49
50
  const aiInteraction = await this.create({
50
51
  userId: userId,
51
52
  threadId: threadId ? threadId : `thread-${userId}`,
52
53
  role: 'human',
53
- message: prompt,
54
+ message: dto.prompt,
54
55
  contentType: '',
55
56
  errorMessage: '',
56
57
  modelUsed: '',
@@ -61,6 +62,7 @@ export class AiInteractionService extends CRUDService<AiInteraction> {
61
62
  const m = {
62
63
  payload: {
63
64
  aiInteractionId: aiInteraction.id,
65
+ moduleName:dto.moduleName
64
66
  },
65
67
  parentEntity: 'aiInteraction',
66
68
  parentEntityId: aiInteraction.id,
@@ -137,6 +139,25 @@ export class AiInteractionService extends CRUDService<AiInteraction> {
137
139
  this.logger.log(`Python script exited with zero exit code: ${stdout}`);
138
140
  const raw: McpResponse = JSON.parse(stdout);
139
141
 
142
+ // Sometimes the raw.response might not be a valid json
143
+ // TODO: examine the content type of the raw response..
144
+ // if (raw.content_type==='json') {
145
+ // }
146
+ let parsedResponse = raw.response;
147
+ try {
148
+ parsedResponse = JSON.parse(raw.response);
149
+ }
150
+ catch (ex) {
151
+ this.logger.warn(`Attempting to parse mcp client response assuming it is JSON, however it is not: ${parsedResponse}`);
152
+ }
153
+ // Parse the response string into an object
154
+ // const parsedResponse = JSON.parse(raw.response);
155
+
156
+ // Replace the string with the parsed object
157
+ const enrichedRaw = {
158
+ ...raw,
159
+ response: parsedResponse,
160
+ };
140
161
  // if (!raw.success) {
141
162
  // return reject(new Error(`MCP error: ${raw.errors?.join(', ')}`));
142
163
  // }
@@ -146,7 +167,7 @@ export class AiInteractionService extends CRUDService<AiInteraction> {
146
167
  // const parsed = JSON.parse(cleaned);
147
168
  // resolve(cleaned);
148
169
 
149
- resolve(raw);
170
+ resolve(enrichedRaw);
150
171
  } catch (err: any) {
151
172
  reject(new Error(`Mcp Invocation Failed: ${err.message}`));
152
173
  }
@@ -986,7 +986,7 @@ export class AuthenticationService {
986
986
  return accessToken;
987
987
  }
988
988
 
989
- async generateRefreshToken(user: User) {
989
+ async generateRefreshToken(user: User, previousRefreshToken?: string) {
990
990
  const refreshTokenId = randomUUID();
991
991
 
992
992
  const refreshToken = await this.signToken(user.id, this.jwtConfiguration.refreshTokenTtl, {
@@ -994,7 +994,7 @@ export class AuthenticationService {
994
994
  })
995
995
 
996
996
  // store the refresh token id in the redis storage.
997
- await this.refreshTokenIdsStorage.insert(user.id, refreshToken);
997
+ await this.refreshTokenIdsStorage.insert(user.id, refreshToken, previousRefreshToken);
998
998
 
999
999
  return refreshToken;
1000
1000
  }
@@ -1164,7 +1164,11 @@ export class AuthenticationService {
1164
1164
  roles: true
1165
1165
  }
1166
1166
  });
1167
- const tokens = await this.generateTokens(user);
1167
+
1168
+ // const tokens = await this.generateTokens(user);
1169
+
1170
+ // Get the refresh token for a user from refresh token storage.
1171
+ const refreshTokenState = await this.refreshTokenIdsStorage.getCurrentRefreshTokenState(user.id);
1168
1172
 
1169
1173
  const response = {
1170
1174
  user: {
@@ -1175,7 +1179,8 @@ export class AuthenticationService {
1175
1179
  id: user.id,
1176
1180
  roles: user.roles.map((role) => role.name)
1177
1181
  },
1178
- ...tokens
1182
+ refreshToken: refreshTokenState.currentRefreshToken,
1183
+ // ...tokens
1179
1184
  }
1180
1185
  return response;
1181
1186
  }
@@ -19,6 +19,10 @@ import { RequestContextService } from './request-context.service';
19
19
  import { ChatterMessageRepository } from 'src/repository/chatter-message.repository';
20
20
  import { lowerFirst } from 'src/helpers/string.helper';
21
21
  import { ModelMetadataHelperService } from 'src/helpers/model-metadata-helper.service';
22
+ import { ChatterMessageDetailsRepository } from 'src/repository/chatter-message-details.repository';
23
+ import { FieldMetadata } from 'src/entities/field-metadata.entity';
24
+ import { CHATTER_MESSAGE_TYPE, CHATTER_MESSAGE_SUBTYPE } from 'src/constants/chatter-message.constants';
25
+ import { classify } from '@angular-devkit/core/src/utils/strings';
22
26
  @Injectable()
23
27
  export class ChatterMessageService extends CRUDService<ChatterMessage> {
24
28
  constructor(
@@ -32,8 +36,10 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
32
36
  readonly entityManager: EntityManager,
33
37
  // @InjectRepository(ChatterMessage, 'default')
34
38
  readonly repo: ChatterMessageRepository,
35
- @InjectRepository(ChatterMessageDetails, 'default')
36
- readonly chatterMessageDetailsRepo: Repository<ChatterMessageDetails>,
39
+ // @InjectRepository(ChatterMessageDetailsRepository, 'default')
40
+ readonly chatterMessageDetailsRepo: ChatterMessageDetailsRepository,
41
+ @InjectRepository(FieldMetadata, 'default')
42
+ readonly fieldMetadataRepo: Repository<FieldMetadata>,
37
43
  readonly moduleRef: ModuleRef,
38
44
  @InjectRepository(ModelMetadata)
39
45
  private readonly modelMetadataRepo: Repository<ModelMetadata>,
@@ -45,8 +51,8 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
45
51
 
46
52
  async postMessage(postDto: PostChatterMessageDto, files: Express.Multer.File[] = []) {
47
53
  const chatterMessage = new ChatterMessage();
48
- chatterMessage.messageType = 'custom';
49
- chatterMessage.messageSubType = postDto.messageSubType || 'post_message';
54
+ chatterMessage.messageType = CHATTER_MESSAGE_TYPE.CUSTOM;
55
+ chatterMessage.messageSubType = postDto.messageSubType || CHATTER_MESSAGE_SUBTYPE.CUSTOM;
50
56
  chatterMessage.messageBody = postDto.messageBody;
51
57
  chatterMessage.coModelEntityId = postDto.coModelEntityId;
52
58
  chatterMessage.coModelName = postDto.coModelName;
@@ -97,7 +103,8 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
97
103
  },
98
104
  relations: {
99
105
  fields: true,
100
- module: true
106
+ module: true,
107
+ userKeyField: true
101
108
  }
102
109
  });
103
110
 
@@ -114,11 +121,13 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
114
121
  const activeUser = this.requestContextService.getActiveUser();
115
122
 
116
123
  const chatterMessage = new ChatterMessage();
117
- chatterMessage.messageType = 'audit';
118
- chatterMessage.messageSubType = 'insert';
124
+ chatterMessage.messageType = CHATTER_MESSAGE_TYPE.AUDIT;
125
+ chatterMessage.messageSubType = CHATTER_MESSAGE_SUBTYPE.AUDIT_INSERT;
119
126
  chatterMessage.coModelEntityId = entity.id;
120
- chatterMessage.coModelName = model.singularName;
121
- chatterMessage.messageBody = `New ${model.displayName} created`;
127
+ chatterMessage.coModelName = model?.singularName;
128
+ chatterMessage.modelDisplayName = model?.displayName;
129
+ chatterMessage.modelUserKey = entity[model?.userKeyField?.name];
130
+ chatterMessage.messageBody = `New ${model?.displayName} created`;
122
131
 
123
132
  if (activeUser) {
124
133
  const userId = activeUser?.sub;
@@ -135,6 +144,7 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
135
144
  const messageDetail = new ChatterMessageDetails();
136
145
  messageDetail.chatterMessage = savedMessage;
137
146
  messageDetail.fieldName = field.name;
147
+ messageDetail.fieldDisplayName = field.displayName;
138
148
  messageDetail.oldValue = null;
139
149
  messageDetail.oldValueDisplay = null;
140
150
  messageDetail.newValue = this.formatFieldValue(field, fieldValue);
@@ -154,7 +164,8 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
154
164
  },
155
165
  relations: {
156
166
  fields: true,
157
- module: true
167
+ module: true,
168
+ userKeyField: true
158
169
  }
159
170
  });
160
171
 
@@ -228,11 +239,13 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
228
239
  const activeUser = this.requestContextService.getActiveUser();
229
240
 
230
241
  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`;
242
+ chatterMessage.messageType = CHATTER_MESSAGE_TYPE.AUDIT;
243
+ chatterMessage.messageSubType = CHATTER_MESSAGE_SUBTYPE.AUDIT_UPDATE;
244
+ chatterMessage.coModelEntityId = entity?.id;
245
+ chatterMessage.coModelName = model?.singularName;
246
+ chatterMessage.modelDisplayName = model.displayName;
247
+ chatterMessage.modelUserKey = entity[model?.userKeyField?.name];
248
+ chatterMessage.messageBody = `${model?.displayName} updated`;
236
249
 
237
250
  if (activeUser) {
238
251
  const userId = activeUser?.sub;
@@ -247,6 +260,7 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
247
260
  const messageDetail = new ChatterMessageDetails();
248
261
  messageDetail.chatterMessage = savedMessage;
249
262
  messageDetail.fieldName = field.name;
263
+ messageDetail.fieldDisplayName = field.displayName;
250
264
  messageDetail.oldValue = this.formatFieldValue(field, oldValue);
251
265
  messageDetail.newValue = this.formatFieldValue(field, newValue);
252
266
  messageDetail.oldValueDisplay = this.formatFieldValueDisplay(field, oldValue);
@@ -262,7 +276,8 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
262
276
  },
263
277
  relations: {
264
278
  fields: true,
265
- module: true
279
+ module: true,
280
+ userKeyField: true
266
281
  }
267
282
  });
268
283
 
@@ -271,11 +286,13 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
271
286
  }
272
287
 
273
288
  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`;
289
+ chatterMessage.messageType = CHATTER_MESSAGE_TYPE.AUDIT;
290
+ chatterMessage.messageSubType = CHATTER_MESSAGE_SUBTYPE.AUDIT_DELETE;
291
+ chatterMessage.coModelEntityId = databaseEntity?.id;
292
+ chatterMessage.coModelName = model?.singularName;
293
+ chatterMessage.modelDisplayName = model?.displayName;
294
+ chatterMessage.modelUserKey = entity[model?.userKeyField?.name];
295
+ chatterMessage.messageBody = `${model?.displayName} deleted`;
279
296
 
280
297
  const activeUser = this.requestContextService.getActiveUser();
281
298
 
@@ -302,7 +319,7 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
302
319
  if (field.relationType === "many-to-one") {
303
320
  return value.id;
304
321
  }
305
- if (field.relationType === 'manyToMany') {
322
+ if (field.relationType === 'many-to-many') {
306
323
  return value.map(item => item.id).join(', ');
307
324
  }
308
325
  }
@@ -324,7 +341,7 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
324
341
  if (field.relationType === "many-to-one") {
325
342
  return value.name;
326
343
  }
327
- if (field.relationType === 'many-toMany') {
344
+ if (field.relationType === 'many-to-many') {
328
345
  return value.map(item => item.name).join(', ');
329
346
  }
330
347
  }
@@ -383,7 +400,7 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
383
400
  return newId !== oldId;
384
401
  }
385
402
 
386
- if (field.relationType === 'many-to-many' || field.relationType === 'manyToMany') {
403
+ if (field.relationType === 'many-to-many') {
387
404
  const newIds = this.extractRelationIds(newValue);
388
405
  const oldIds = this.extractRelationIds(oldValue);
389
406
 
@@ -469,4 +486,106 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
469
486
 
470
487
  return populatedEntity;
471
488
  }
489
+
490
+ async getChatterMessages(
491
+ entityId: number,
492
+ entityName: string,
493
+ query: any
494
+ ) {
495
+ const { limit = 25, offset = 0, sort, populate = [] } = query;
496
+
497
+ const model = await this.modelMetadataRepo.findOne({
498
+ where: {
499
+ singularName: entityName
500
+ },
501
+ });
502
+ const oneToManyFields = await this.fieldMetadataRepo.find({
503
+ where: {
504
+ model: { id: model.id },
505
+ type: 'relation',
506
+ relationType: 'one-to-many'
507
+ }
508
+ });
509
+
510
+ const relatedEntitiesMap = new Map<string, number[]>();
511
+
512
+ for (const field of oneToManyFields) {
513
+ const coModelName = field.relationCoModelSingularName;
514
+ const coModelFieldName = field.relationCoModelFieldName;
515
+
516
+ const coModel = await this.modelMetadataRepo.findOne({
517
+ where: { singularName: coModelName }
518
+ });
519
+
520
+ if (coModel) {
521
+ const relatedEntityRepository = this.entityManager.getRepository(classify(coModelName));
522
+
523
+ const relatedEntities = await relatedEntityRepository.find({
524
+ where: { [coModelFieldName]: { id: entityId } }
525
+ });
526
+
527
+ const relatedIds = relatedEntities.map((entity: any) => entity.id);
528
+ relatedEntitiesMap.set(field.name, relatedIds);
529
+ }
530
+ }
531
+
532
+ const qb = this.repo.createQueryBuilder('entity');
533
+
534
+ const orConditions: string[] = [];
535
+ const parameters: any = {};
536
+
537
+ orConditions.push('(entity.coModelName = :entityName AND entity.coModelEntityId = :entityId)');
538
+ parameters.entityName = entityName;
539
+ parameters.entityId = entityId;
540
+
541
+ let paramIndex = 0;
542
+ for (const [fieldName, relatedIds] of relatedEntitiesMap.entries()) {
543
+ if (relatedIds.length > 0) {
544
+ const field = oneToManyFields.find(f => f.name === fieldName);
545
+ if (field) {
546
+ const coModelName = field.relationCoModelSingularName;
547
+ const idsParamName = `relatedIds${paramIndex}`;
548
+ orConditions.push(`(entity.coModelName = :coModelName${paramIndex} AND entity.coModelEntityId IN (:...${idsParamName}))`);
549
+ parameters[`coModelName${paramIndex}`] = coModelName;
550
+ parameters[idsParamName] = relatedIds;
551
+ paramIndex++;
552
+ }
553
+ }
554
+ }
555
+
556
+ qb.where(orConditions.join(' OR '), parameters);
557
+
558
+ const relations = ['chatterMessageDetails', 'user'];
559
+ if (populate && populate.length > 0) {
560
+ const normalizedPopulate = this.crudHelperService.normalize(populate);
561
+ relations.push(...normalizedPopulate.filter(rel => !relations.includes(rel)));
562
+ }
563
+
564
+ relations.forEach(relation => {
565
+ qb.leftJoinAndSelect(`entity.${relation}`, relation);
566
+ });
567
+
568
+ qb.orderBy('entity.createdAt', 'DESC');
569
+
570
+ qb.skip(offset).take(limit);
571
+
572
+ const [entities, count] = await qb.getManyAndCount();
573
+
574
+ const currentPage = Math.floor(offset / limit) + 1;
575
+ const totalPages = Math.ceil(count / limit);
576
+ const nextPage = currentPage < totalPages ? currentPage + 1 : null;
577
+ const prevPage = currentPage > 1 ? currentPage - 1 : null;
578
+
579
+ return {
580
+ meta: {
581
+ totalRecords: count,
582
+ currentPage: currentPage,
583
+ nextPage: nextPage,
584
+ prevPage: prevPage,
585
+ totalPages: totalPages,
586
+ perPage: +limit,
587
+ },
588
+ records: entities
589
+ };
590
+ }
472
591
  }
@@ -28,13 +28,13 @@ export class RefreshTokenIdsStorageService {
28
28
  private readonly authenticationService: AuthenticationService
29
29
  ) { }
30
30
 
31
- async insert(userId: number, refreshToken: string): Promise<void> {
31
+ async insert(userId: number, refreshToken: string, previousRefreshToken?: string): Promise<void> {
32
32
  // TODO: save a refresh token object with this shape {"currentRefreshToken": "", "previousRefreshToken": ""}
33
33
  // Save a refresh token object with the shape: { currentRefreshToken: string, previousRefreshToken: string }
34
34
  const existing = (await this.cacheManager.get(this.getKey(userId))) as { currentRefreshToken?: string, previousRefreshToken?: string } | undefined;
35
35
  const refreshTokenState = {
36
36
  currentRefreshToken: refreshToken,
37
- previousRefreshToken: "",
37
+ previousRefreshToken: previousRefreshToken ?? "",
38
38
  };
39
39
  await this.cacheManager.set(this.getKey(userId), refreshTokenState);
40
40
  }
@@ -84,14 +84,14 @@ export class RefreshTokenIdsStorageService {
84
84
  // Scenario 1: Token matches currentRefreshToken
85
85
  valid = true;
86
86
  // Rotate tokens: move current to previous, set new current (simulate generation)
87
- newRefreshToken = await this.authenticationService.generateRefreshToken(user); // Replace with real token generation logic
87
+ newRefreshToken = await this.authenticationService.generateRefreshToken(user, refreshTokenState.currentRefreshToken); // Replace with real token generation logic
88
88
 
89
89
 
90
90
  // updated cache state
91
- await this.cacheManager.set(this.getKey(user.id), {
92
- currentRefreshToken: newRefreshToken,
93
- previousRefreshToken: refreshTokenState.currentRefreshToken,
94
- });
91
+ // await this.cacheManager.set(this.getKey(user.id), {
92
+ // currentRefreshToken: newRefreshToken,
93
+ // previousRefreshToken: refreshTokenState.currentRefreshToken,
94
+ // });
95
95
 
96
96
  // Optionally, set a timeout to clear previousRefreshToken after X minutes
97
97
  setTimeout(async () => {
@@ -125,4 +125,8 @@ export class RefreshTokenIdsStorageService {
125
125
  private getKey(userId: number): string {
126
126
  return `user-${userId}`;
127
127
  }
128
+
129
+ getCurrentRefreshTokenState(userId: number): Promise<any> {
130
+ return this.cacheManager.get(this.getKey(userId));
131
+ }
128
132
  }