@solidstarters/solid-core 1.2.111 → 1.2.112

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 (204) hide show
  1. package/dist/controllers/model-metadata.controller.d.ts +1 -1
  2. package/dist/controllers/model-metadata.controller.d.ts.map +1 -1
  3. package/dist/controllers/model-metadata.controller.js +1 -1
  4. package/dist/controllers/model-metadata.controller.js.map +1 -1
  5. package/dist/controllers/queues-test.controller.d.ts +5 -3
  6. package/dist/controllers/queues-test.controller.d.ts.map +1 -1
  7. package/dist/controllers/queues-test.controller.js +20 -8
  8. package/dist/controllers/queues-test.controller.js.map +1 -1
  9. package/dist/entities/field-metadata.entity.js +1 -1
  10. package/dist/entities/mq-message.entity.d.ts +1 -0
  11. package/dist/entities/mq-message.entity.d.ts.map +1 -1
  12. package/dist/entities/mq-message.entity.js +5 -1
  13. package/dist/entities/mq-message.entity.js.map +1 -1
  14. package/dist/helpers/module-metadata-helper.service.d.ts +1 -0
  15. package/dist/helpers/module-metadata-helper.service.d.ts.map +1 -1
  16. package/dist/helpers/module-metadata-helper.service.js +9 -4
  17. package/dist/helpers/module-metadata-helper.service.js.map +1 -1
  18. package/dist/index.d.ts +3 -3
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +3 -3
  21. package/dist/index.js.map +1 -1
  22. package/dist/interfaces.d.ts +2 -1
  23. package/dist/interfaces.d.ts.map +1 -1
  24. package/dist/interfaces.js +1 -0
  25. package/dist/interfaces.js.map +1 -1
  26. package/dist/jobs/api-email-publisher.service.d.ts +1 -1
  27. package/dist/jobs/api-email-publisher.service.d.ts.map +1 -1
  28. package/dist/jobs/api-email-publisher.service.js +1 -1
  29. package/dist/jobs/api-email-publisher.service.js.map +1 -1
  30. package/dist/jobs/api-email-subscriber.service.d.ts +1 -1
  31. package/dist/jobs/api-email-subscriber.service.d.ts.map +1 -1
  32. package/dist/jobs/api-email-subscriber.service.js +1 -1
  33. package/dist/jobs/api-email-subscriber.service.js.map +1 -1
  34. package/dist/jobs/database/generate-code-publisher.service.d.ts +11 -0
  35. package/dist/jobs/database/generate-code-publisher.service.d.ts.map +1 -0
  36. package/dist/jobs/database/generate-code-publisher.service.js +39 -0
  37. package/dist/jobs/database/generate-code-publisher.service.js.map +1 -0
  38. package/dist/jobs/database/generate-code-queue-options.d.ts +8 -0
  39. package/dist/jobs/database/generate-code-queue-options.d.ts.map +1 -0
  40. package/dist/jobs/database/generate-code-queue-options.js +10 -0
  41. package/dist/jobs/database/generate-code-queue-options.js.map +1 -0
  42. package/dist/jobs/database/generate-code-subscriber.service.d.ts +16 -0
  43. package/dist/jobs/database/generate-code-subscriber.service.d.ts.map +1 -0
  44. package/dist/jobs/database/generate-code-subscriber.service.js +67 -0
  45. package/dist/jobs/database/generate-code-subscriber.service.js.map +1 -0
  46. package/dist/jobs/database/queue-test-db-publisher.service.d.ts +11 -0
  47. package/dist/jobs/database/queue-test-db-publisher.service.d.ts.map +1 -0
  48. package/dist/jobs/database/queue-test-db-publisher.service.js +39 -0
  49. package/dist/jobs/database/queue-test-db-publisher.service.js.map +1 -0
  50. package/dist/jobs/database/queue-test-db-subscriber.service.d.ts +14 -0
  51. package/dist/jobs/database/queue-test-db-subscriber.service.d.ts.map +1 -0
  52. package/dist/jobs/database/queue-test-db-subscriber.service.js +50 -0
  53. package/dist/jobs/database/queue-test-db-subscriber.service.js.map +1 -0
  54. package/dist/jobs/database/test-queue-db-options.d.ts +8 -0
  55. package/dist/jobs/database/test-queue-db-options.d.ts.map +1 -0
  56. package/dist/jobs/database/test-queue-db-options.js +10 -0
  57. package/dist/jobs/database/test-queue-db-options.js.map +1 -0
  58. package/dist/jobs/email-publisher.service.d.ts +1 -1
  59. package/dist/jobs/email-publisher.service.d.ts.map +1 -1
  60. package/dist/jobs/email-publisher.service.js +1 -1
  61. package/dist/jobs/email-publisher.service.js.map +1 -1
  62. package/dist/jobs/email-subscriber.service.d.ts +1 -1
  63. package/dist/jobs/email-subscriber.service.d.ts.map +1 -1
  64. package/dist/jobs/email-subscriber.service.js +1 -1
  65. package/dist/jobs/email-subscriber.service.js.map +1 -1
  66. package/dist/jobs/otp-publisher.service.d.ts +1 -1
  67. package/dist/jobs/otp-publisher.service.d.ts.map +1 -1
  68. package/dist/jobs/otp-publisher.service.js +1 -1
  69. package/dist/jobs/otp-publisher.service.js.map +1 -1
  70. package/dist/jobs/otp-subscriber.service.d.ts +1 -1
  71. package/dist/jobs/otp-subscriber.service.d.ts.map +1 -1
  72. package/dist/jobs/otp-subscriber.service.js +1 -1
  73. package/dist/jobs/otp-subscriber.service.js.map +1 -1
  74. package/dist/jobs/queue-test-publisher.service.d.ts +1 -1
  75. package/dist/jobs/queue-test-publisher.service.d.ts.map +1 -1
  76. package/dist/jobs/queue-test-publisher.service.js +3 -3
  77. package/dist/jobs/queue-test-publisher.service.js.map +1 -1
  78. package/dist/jobs/queue-test-subscriber.service.d.ts +2 -2
  79. package/dist/jobs/queue-test-subscriber.service.d.ts.map +1 -1
  80. package/dist/jobs/queue-test-subscriber.service.js +9 -3
  81. package/dist/jobs/queue-test-subscriber.service.js.map +1 -1
  82. package/dist/jobs/sms-publisher.service.d.ts +1 -1
  83. package/dist/jobs/sms-publisher.service.d.ts.map +1 -1
  84. package/dist/jobs/sms-publisher.service.js +1 -1
  85. package/dist/jobs/sms-publisher.service.js.map +1 -1
  86. package/dist/jobs/sms-subscriber.service.d.ts +1 -1
  87. package/dist/jobs/sms-subscriber.service.d.ts.map +1 -1
  88. package/dist/jobs/sms-subscriber.service.js +1 -1
  89. package/dist/jobs/sms-subscriber.service.js.map +1 -1
  90. package/dist/jobs/{test-queue.config.d.ts → test-queue-options.d.ts} +1 -1
  91. package/dist/jobs/test-queue-options.d.ts.map +1 -0
  92. package/dist/jobs/{test-queue.config.js → test-queue-options.js} +1 -1
  93. package/dist/jobs/test-queue-options.js.map +1 -0
  94. package/dist/jobs/whatsapp-publisher.service.d.ts +1 -1
  95. package/dist/jobs/whatsapp-publisher.service.d.ts.map +1 -1
  96. package/dist/jobs/whatsapp-publisher.service.js +1 -1
  97. package/dist/jobs/whatsapp-publisher.service.js.map +1 -1
  98. package/dist/jobs/whatsapp-subscriber.service.d.ts +1 -1
  99. package/dist/jobs/whatsapp-subscriber.service.d.ts.map +1 -1
  100. package/dist/jobs/whatsapp-subscriber.service.js +1 -1
  101. package/dist/jobs/whatsapp-subscriber.service.js.map +1 -1
  102. package/dist/seeders/seed-data/solid-core-metadata.json +342 -282
  103. package/dist/services/field-metadata.service.d.ts.map +1 -1
  104. package/dist/services/field-metadata.service.js +4 -1
  105. package/dist/services/field-metadata.service.js.map +1 -1
  106. package/dist/services/model-metadata.service.d.ts +11 -4
  107. package/dist/services/model-metadata.service.d.ts.map +1 -1
  108. package/dist/services/model-metadata.service.js +141 -6
  109. package/dist/services/model-metadata.service.js.map +1 -1
  110. package/dist/services/mq-message-queue.service.d.ts.map +1 -1
  111. package/dist/services/mq-message-queue.service.js +1 -0
  112. package/dist/services/mq-message-queue.service.js.map +1 -1
  113. package/dist/services/mq-message.service.d.ts +2 -0
  114. package/dist/services/mq-message.service.d.ts.map +1 -1
  115. package/dist/services/mq-message.service.js +29 -2
  116. package/dist/services/mq-message.service.js.map +1 -1
  117. package/dist/services/queues/database-publisher.service.d.ts +16 -0
  118. package/dist/services/queues/database-publisher.service.d.ts.map +1 -0
  119. package/dist/services/queues/database-publisher.service.js +59 -0
  120. package/dist/services/queues/database-publisher.service.js.map +1 -0
  121. package/dist/services/queues/database-subscriber.service.d.ts +21 -0
  122. package/dist/services/queues/database-subscriber.service.d.ts.map +1 -0
  123. package/dist/services/queues/database-subscriber.service.js +128 -0
  124. package/dist/services/queues/database-subscriber.service.js.map +1 -0
  125. package/dist/services/{rabbitmq-publisher.service.d.ts → queues/rabbitmq-publisher.service.d.ts} +6 -4
  126. package/dist/services/queues/rabbitmq-publisher.service.d.ts.map +1 -0
  127. package/dist/services/{rabbitmq-publisher.service.js → queues/rabbitmq-publisher.service.js} +14 -1
  128. package/dist/services/queues/rabbitmq-publisher.service.js.map +1 -0
  129. package/dist/services/{rabbitmq-subscriber.service.d.ts → queues/rabbitmq-subscriber.service.d.ts} +6 -4
  130. package/dist/services/queues/rabbitmq-subscriber.service.d.ts.map +1 -0
  131. package/dist/services/{rabbitmq-subscriber.service.js → queues/rabbitmq-subscriber.service.js} +14 -10
  132. package/dist/services/queues/rabbitmq-subscriber.service.js.map +1 -0
  133. package/dist/services/queues/redis-publisher.service.d.ts +1 -0
  134. package/dist/services/queues/redis-publisher.service.d.ts.map +1 -0
  135. package/dist/services/queues/redis-publisher.service.js +1 -0
  136. package/dist/services/queues/redis-publisher.service.js.map +1 -0
  137. package/dist/services/queues/redis-subscriber.service.d.ts +1 -0
  138. package/dist/services/queues/redis-subscriber.service.d.ts.map +1 -0
  139. package/dist/services/queues/redis-subscriber.service.js +1 -0
  140. package/dist/services/queues/redis-subscriber.service.js.map +1 -0
  141. package/dist/services/sms/Msg91BaseSMSService.d.ts +1 -1
  142. package/dist/services/sms/Msg91BaseSMSService.d.ts.map +1 -1
  143. package/dist/solid-core.module.d.ts.map +1 -1
  144. package/dist/solid-core.module.js +10 -2
  145. package/dist/solid-core.module.js.map +1 -1
  146. package/dist/subscribers/field-metadata.subscriber.d.ts +10 -0
  147. package/dist/subscribers/field-metadata.subscriber.d.ts.map +1 -0
  148. package/dist/subscribers/field-metadata.subscriber.js +40 -0
  149. package/dist/subscribers/field-metadata.subscriber.js.map +1 -0
  150. package/dist/subscribers/{model.subscriber.d.ts → model-metadata.subscriber.d.ts} +2 -2
  151. package/dist/subscribers/model-metadata.subscriber.d.ts.map +1 -0
  152. package/dist/subscribers/{model.subscriber.js → model-metadata.subscriber.js} +8 -8
  153. package/dist/subscribers/model-metadata.subscriber.js.map +1 -0
  154. package/dist/tsconfig.tsbuildinfo +1 -1
  155. package/package.json +1 -1
  156. package/src/controllers/model-metadata.controller.ts +1 -2
  157. package/src/controllers/queues-test.controller.ts +15 -6
  158. package/src/entities/field-metadata.entity.ts +1 -1
  159. package/src/entities/mq-message.entity.ts +3 -0
  160. package/src/helpers/module-metadata-helper.service.ts +11 -5
  161. package/src/index.ts +3 -3
  162. package/src/interfaces.ts +1 -0
  163. package/src/jobs/api-email-publisher.service.ts +1 -1
  164. package/src/jobs/api-email-subscriber.service.ts +1 -1
  165. package/src/jobs/database/generate-code-publisher.service.ts +23 -0
  166. package/src/jobs/database/generate-code-queue-options.ts +9 -0
  167. package/src/jobs/database/generate-code-subscriber.service.ts +56 -0
  168. package/src/jobs/database/queue-test-db-publisher.service.ts +24 -0
  169. package/src/jobs/database/queue-test-db-subscriber.service.ts +38 -0
  170. package/src/jobs/database/test-queue-db-options.ts +13 -0
  171. package/src/jobs/email-publisher.service.ts +1 -1
  172. package/src/jobs/email-subscriber.service.ts +1 -1
  173. package/src/jobs/otp-publisher.service.ts +1 -1
  174. package/src/jobs/otp-subscriber.service.ts +1 -1
  175. package/src/jobs/queue-test-publisher.service.ts +2 -2
  176. package/src/jobs/queue-test-subscriber.service.ts +10 -2
  177. package/src/jobs/sms-publisher.service.ts +1 -1
  178. package/src/jobs/sms-subscriber.service.ts +1 -1
  179. package/src/jobs/whatsapp-publisher.service.ts +1 -1
  180. package/src/jobs/whatsapp-subscriber.service.ts +1 -1
  181. package/src/seeders/seed-data/solid-core-metadata.json +344 -284
  182. package/src/services/field-metadata.service.ts +5 -5
  183. package/src/services/model-metadata.service.ts +256 -13
  184. package/src/services/mq-message-queue.service.ts +2 -1
  185. package/src/services/mq-message.service.ts +47 -8
  186. package/src/services/queues/database-publisher.service.ts +79 -0
  187. package/src/services/queues/database-subscriber.service.ts +173 -0
  188. package/src/services/{rabbitmq-publisher.service.ts → queues/rabbitmq-publisher.service.ts} +24 -8
  189. package/src/services/{rabbitmq-subscriber.service.ts → queues/rabbitmq-subscriber.service.ts} +23 -16
  190. package/src/services/queues/redis-publisher.service.ts +0 -0
  191. package/src/services/queues/redis-subscriber.service.ts +0 -0
  192. package/src/services/sms/Msg91BaseSMSService.ts +1 -1
  193. package/src/solid-core.module.ts +10 -2
  194. package/src/subscribers/field-metadata.subscriber.ts +52 -0
  195. package/src/subscribers/{model.subscriber.ts → model-metadata.subscriber.ts} +2 -2
  196. package/dist/jobs/test-queue.config.d.ts.map +0 -1
  197. package/dist/jobs/test-queue.config.js.map +0 -1
  198. package/dist/services/rabbitmq-publisher.service.d.ts.map +0 -1
  199. package/dist/services/rabbitmq-publisher.service.js.map +0 -1
  200. package/dist/services/rabbitmq-subscriber.service.d.ts.map +0 -1
  201. package/dist/services/rabbitmq-subscriber.service.js.map +0 -1
  202. package/dist/subscribers/model.subscriber.d.ts.map +0 -1
  203. package/dist/subscribers/model.subscriber.js.map +0 -1
  204. /package/src/jobs/{test-queue.config.ts → test-queue-options.ts} +0 -0
@@ -156,16 +156,13 @@ export class FieldMetadataService {
156
156
  private async saveInverseField(fieldRepository: Repository<FieldMetadata>, relationModel: ModelMetadata, inverseField: FieldMetadata): Promise<FieldMetadata> {
157
157
  const existingInverseField = await fieldRepository.findOne({
158
158
  where: {
159
- model: relationModel,
159
+ model: { id: relationModel.id },
160
160
  name: inverseField.name
161
161
  }
162
162
  });
163
163
 
164
164
  if (existingInverseField) {
165
- // const updatedField = fieldRepository.merge(existingInverseField, inverseField);
166
- // const savedField = await fieldRepository.save(updatedField);
167
- // return savedField;
168
- return existingInverseField;
165
+ return existingInverseField; // No need to update if it already exists
169
166
  }
170
167
  else {
171
168
  const savedField = await fieldRepository.save(fieldRepository.create(inverseField));
@@ -1100,6 +1097,9 @@ export class FieldMetadataService {
1100
1097
  fieldObject.mediaStorageProviderUserKey = field.mediaStorageProvider.name
1101
1098
  }
1102
1099
  }
1100
+ if (field.isMarkedForRemoval) {
1101
+ fieldObject.isMarkedForRemoval = field.isMarkedForRemoval;
1102
+ }
1103
1103
 
1104
1104
  return fieldObject;
1105
1105
  }
@@ -1,7 +1,7 @@
1
1
  import { BadRequestException, Injectable, Logger, NotFoundException } from '@nestjs/common';
2
2
  import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
3
3
  import * as fs from 'fs/promises'; // Use the Promise-based version of fs for async/await
4
- import { DataSource, EntityManager, Repository, SelectQueryBuilder } from 'typeorm';
4
+ import { DataSource, EntityManager, In, Repository, SelectQueryBuilder } from 'typeorm';
5
5
  import { CreateModelMetadataDto } from '../dtos/create-model-metadata.dto';
6
6
  import { ModelMetadata } from '../entities/model-metadata.entity';
7
7
  import { ModuleMetadata } from '../entities/module-metadata.entity';
@@ -24,6 +24,9 @@ import { CrudHelperService } from './crud-helper.service';
24
24
  import { FieldMetadataService } from './field-metadata.service';
25
25
  import { MediaStorageProviderMetadataService } from './media-storage-provider-metadata.service';
26
26
  import { RoleMetadataService } from './role-metadata.service';
27
+ import { GenerateCodePublisher } from 'src/jobs/database/generate-code-publisher.service';
28
+ import { PermissionMetadata } from 'src/entities/permission-metadata.entity';
29
+ import { classify, dasherize } from '@angular-devkit/core/src/utils/strings';
27
30
 
28
31
  @Injectable()
29
32
  export class ModelMetadataService {
@@ -41,6 +44,7 @@ export class ModelMetadataService {
41
44
  private readonly fieldMetadataService: FieldMetadataService,
42
45
  private readonly roleService: RoleMetadataService,
43
46
  private readonly moduleMetadataHelperService: ModuleMetadataHelperService,
47
+ private readonly generateCodePublihser: GenerateCodePublisher,
44
48
  ) { }
45
49
 
46
50
  async findMany(basicFilterDto: BasicFilterDto) {
@@ -262,6 +266,9 @@ export class ModelMetadataService {
262
266
  isChild: model?.isChild,
263
267
  parentModelUserKey: model?.parentModel?.singularName,
264
268
  enableAuditTracking: model?.enableAuditTracking,
269
+ enableSoftDelete: model?.enableSoftDelete,
270
+ draftPublishWorkflow: model?.draftPublishWorkflow,
271
+ internationalisation: model?.internationalisation,
265
272
  fields: []
266
273
  }
267
274
 
@@ -285,7 +292,6 @@ export class ModelMetadataService {
285
292
  }
286
293
  }
287
294
 
288
-
289
295
  async updateInDb(manager: EntityManager, id: number, updateModelMetaDataDto: UpdateModelMetaDataDto) {
290
296
 
291
297
  const { fields: fieldsMetadata, ...modelMetaDataWithoutFields } = updateModelMetaDataDto;
@@ -395,8 +401,6 @@ export class ModelMetadataService {
395
401
 
396
402
  }
397
403
 
398
-
399
-
400
404
  async updateInFile(modelId: any, repo: Repository<ModelMetadata>) {
401
405
  try {
402
406
 
@@ -405,6 +409,11 @@ export class ModelMetadataService {
405
409
  id: modelId,
406
410
  },
407
411
  relations: ["fields", "fields.mediaStorageProvider", "module", "parentModel"], //FIXME: Check with jenender and change to relations to avoid confusion
412
+ order: {
413
+ fields: {
414
+ id: "ASC",
415
+ },
416
+ },
408
417
  });
409
418
 
410
419
  const filePath = await this.moduleMetadataHelperService.getModuleMetadataFilePath(model.module.name);
@@ -422,14 +431,20 @@ export class ModelMetadataService {
422
431
  isChild: model?.isChild,
423
432
  parentModelUserKey: model?.parentModel?.singularName,
424
433
  enableAuditTracking: model?.enableAuditTracking,
434
+ enableSoftDelete: model?.enableSoftDelete,
435
+ draftPublishWorkflow: model?.draftPublishWorkflow,
436
+ internationalisation: model?.internationalisation,
425
437
  fields: []
426
438
  }
427
439
 
428
440
  for (let i = 0; i < model.fields.length; i++) {
429
441
  const field = model.fields[i];
430
- if (!field.isSystem && !field.isMarkedForRemoval) {
442
+ if (!field.isSystem) {
431
443
 
432
444
  const fieldObject: Record<string, any> = await this.fieldMetadataService.createFieldConfig(field);
445
+ if (field.isMarkedForRemoval) {
446
+ fieldObject.isMarkedForRemoval = true;
447
+ }
433
448
  modelMetaData.fields.push(fieldObject);
434
449
  }
435
450
  }
@@ -482,7 +497,9 @@ export class ModelMetadataService {
482
497
  async removeBySingularName(singularName: string) {
483
498
  try {
484
499
  const entity = await this.findOneBySingularName(singularName);
485
- return this.modelMetadataRepo.remove(entity);
500
+ await this.cleanupOnDelete(entity.id);
501
+ const r = await this.modelMetadataRepo.remove(entity);
502
+ return r;
486
503
  } catch (error) {
487
504
  }
488
505
  }
@@ -503,19 +520,218 @@ export class ModelMetadataService {
503
520
  // if (!entity) {
504
521
  // throw new Error(`Entity with id ${ id } not found`);
505
522
  // }
506
- removedEntities.push(await this.modelMetadataRepo.remove(entity));
523
+ await this.cleanupOnDelete(entity.id);
524
+ const r = await this.modelMetadataRepo.remove(entity);
525
+ removedEntities.push(r);
507
526
  }
508
527
 
509
528
  return removedEntities
510
529
  }
511
530
 
512
-
513
531
  async remove(id: number) {
514
532
  const entity = await this.findOne(id);
515
- return this.modelMetadataRepo.remove(entity);
533
+ await this.cleanupOnDelete(entity.id);
534
+ const r = await this.modelMetadataRepo.remove(entity);
535
+ return r
516
536
  }
517
537
 
518
- async handleGenerateCode(options: CodeGenerationOptions): Promise<string> {
538
+ async cleanupOnDelete(modelEntityId: number) {
539
+ const modelEntity = await this.modelMetadataRepo.findOne({
540
+ where: {
541
+ // @ts-ignore
542
+ id: modelEntityId,
543
+ },
544
+ relations: ['module']
545
+ });
546
+
547
+ if (!modelEntity) {
548
+ this.logger.log(`Invalid modelEntityId: ${modelEntityId} unable to resolve model metadata`);
549
+ return;
550
+ }
551
+ if (modelEntity.id !== modelEntityId) {
552
+ this.logger.log(`Invalid modelEntityId: ${modelEntityId} unable to resolve model metadata id ${modelEntity.id} not matching with the one passed as argument ${modelEntityId}`);
553
+ return;
554
+ }
555
+
556
+ this.logger.log(`Cleaning up for model: ${modelEntity.singularName} belonging to module: ${modelEntity.module.name}`);
557
+
558
+ const modulePath = await this.moduleMetadataHelperService.getModulePath(modelEntity.module.name);
559
+ // /Users/harishpatel/Code/javascript/school-fees-portal/solid-api/src/solid-core
560
+ this.logger.log(`Module path: ${modulePath}`);
561
+
562
+ const filesToDelete = [];
563
+ // <singularName>.entity.ts | The TypeORM model that needs to be deleted. | Automatic
564
+ const entityFilePath = `${modulePath}/entities/${dasherize(modelEntity.singularName)}.entity.ts`;
565
+ filesToDelete.push(entityFilePath);
566
+ this.logger.log(`About to delete entity file path: ${entityFilePath}`);
567
+
568
+ // <singularName>.create.dto.ts | The TypeORM model that needs to be deleted. | Automatic
569
+ const createDtoFilePath = `${modulePath}/dtos/create-${dasherize(modelEntity.singularName)}.dto.ts`;
570
+ filesToDelete.push(createDtoFilePath);
571
+ this.logger.log(`About to delete create DTO file path: ${createDtoFilePath}`);
572
+
573
+ // <singularName>.update.dto.ts | The TypeORM model that needs to be deleted. | Automatic
574
+ const updateDtoFilePath = `${modulePath}/dtos/update-${dasherize(modelEntity.singularName)}.dto.ts`;
575
+ filesToDelete.push(updateDtoFilePath);
576
+ this.logger.log(`About to delete update DTO file path: ${updateDtoFilePath}`);
577
+
578
+ // <singularName>.repository.ts | The TypeORM model that needs to be deleted. | Automatic
579
+ const repositoryFilePath = `${modulePath}/repositories/${dasherize(modelEntity.singularName)}.repository.ts`;
580
+ filesToDelete.push(repositoryFilePath);
581
+ this.logger.log(`About to delete repository file path: ${repositoryFilePath}`);
582
+
583
+ // <singularName>.service.ts | The TypeORM model that needs to be deleted. | Automatic
584
+ const serviceFilePath = `${modulePath}/services/${dasherize(modelEntity.singularName)}.service.ts`;
585
+ filesToDelete.push(serviceFilePath);
586
+ this.logger.log(`About to delete service file path: ${serviceFilePath}`);
587
+
588
+ // <singularName>.controller.ts | The TypeORM model that needs to be deleted. | Automatic
589
+ const controllerFilePath = `${modulePath}/controllers/${dasherize(modelEntity.singularName)}.controller.ts`;
590
+ filesToDelete.push(controllerFilePath);
591
+ this.logger.log(`About to delete controller file path: ${controllerFilePath}`);
592
+
593
+ for (let i = 0; i < filesToDelete.length; i++) {
594
+ const fileToDelete = filesToDelete[i];
595
+ try {
596
+ await fs.unlink(fileToDelete);
597
+ this.logger.log(`Deleted file: ${fileToDelete}`);
598
+ } catch (error) {
599
+ this.logger.error(`Error deleting file: ${fileToDelete}`, error);
600
+ }
601
+ }
602
+
603
+ // Delete the permissions, menu, actions & views related to this model.
604
+ const controllerName = `${classify(modelEntity.singularName)}Controller`;
605
+ const permissionNames = [
606
+ `${controllerName}.delete`,
607
+ `${controllerName}.deleteMany`,
608
+ `${controllerName}.findOne`,
609
+ `${controllerName}.findMany`,
610
+ `${controllerName}.recover`,
611
+ `${controllerName}.recoverMany`,
612
+ `${controllerName}.partialUpdate`,
613
+ `${controllerName}.update`,
614
+ `${controllerName}.insertMany`,
615
+ `${controllerName}.create`,
616
+ ];
617
+ const permissionsRepo = this.dataSource.getRepository(PermissionMetadata);
618
+ const permissionsToDelete = await permissionsRepo.find({
619
+ where: { name: In(permissionNames) },
620
+ relations: ['roles'],
621
+ });
622
+
623
+ // Remove role associations first
624
+ for (const permission of permissionsToDelete) {
625
+ if (permission.roles?.length) {
626
+ await this.dataSource
627
+ .createQueryBuilder()
628
+ .relation(PermissionMetadata, 'roles')
629
+ .of(permission) // permission instance or its ID
630
+ .remove(permission.roles); // remove all linked roles
631
+ }
632
+ }
633
+
634
+ await permissionsRepo.remove(permissionsToDelete);
635
+
636
+ // Delete actions
637
+ const actionRepo = this.dataSource.getRepository(ActionMetadata);
638
+ const action = await actionRepo.findOne({ where: { model: { id: modelEntity.id } } });
639
+ await actionRepo.delete({ model: { id: modelEntity.id } });
640
+
641
+ // Delete menu items
642
+ const menuItemRepo = this.dataSource.getRepository(MenuItemMetadata);
643
+ if (action) {
644
+ const menuItems = await menuItemRepo.find({ where: { action: { id: action.id } } });
645
+ for (let i = 0; i < menuItems.length; i++) {
646
+ const menuItem = menuItems[i];
647
+ await menuItemRepo.remove(menuItem);
648
+ }
649
+ }
650
+
651
+ // Delete view
652
+ const viewRepo = this.dataSource.getRepository(ViewMetadata);
653
+ await viewRepo.delete({ model: { id: modelEntity.id } })
654
+
655
+ // <moduleName>-metadata.json | Remove references to this model in the model metadata, menu, action & view sections. | Automatic
656
+ const filePath = await this.moduleMetadataHelperService.getModuleMetadataFilePath(modelEntity.module.name);
657
+ const metaData = await this.moduleMetadataHelperService.getModuleMetadataConfiguration(filePath);
658
+ const existingModelIndex = metaData.moduleMetadata.models.findIndex(
659
+ (existingModel: any) => existingModel.singularName === modelEntity.singularName
660
+ );
661
+ if (existingModelIndex !== -1) {
662
+ metaData.moduleMetadata.models.splice(existingModelIndex, 1);
663
+ }
664
+ const updatedContent = JSON.stringify(metaData, null, 2);
665
+ await fs.writeFile(filePath, updatedContent);
666
+
667
+ // <moduleName>.module.ts | Remove all references and imports of the above files. | Manual (X)
668
+ // const moduleFilePath = path.resolve(modulePath, `${dasherize(modelEntity.module.name)}.module.ts`);
669
+
670
+ // this.logger.log(`Working on module file ${moduleFilePath}`);
671
+ // const project = new Project();
672
+ // const sourceFile = project.addSourceFileAtPath(moduleFilePath);
673
+
674
+ // // Remove import declarations related to deleted files
675
+ // sourceFile.getImportDeclarations().forEach(importDecl => {
676
+ // const moduleSpecifier = importDecl.getModuleSpecifierValue();
677
+ // const resolvedPath = importDecl.getModuleSpecifierSourceFile()?.getFilePath() || '';
678
+ // if (filesToDelete.some(file => resolvedPath.endsWith(file))) {
679
+ // importDecl.remove();
680
+ // }
681
+ // });
682
+
683
+ // // Remove identifiers from `@Module` metadata (imports, providers, controllers)
684
+ // const moduleDecorator = sourceFile.getFirstDescendantByKind(SyntaxKind.Decorator);
685
+ // const objectLiteral = moduleDecorator?.getCallExpression()?.getArguments()?.[0];
686
+
687
+ // if (objectLiteral && objectLiteral.getKind() === SyntaxKind.ObjectLiteralExpression) {
688
+ // const objectLiteralExpr = objectLiteral.asKindOrThrow(SyntaxKind.ObjectLiteralExpression);
689
+
690
+ // for (const propName of ['imports', 'providers', 'controllers', 'exports']) {
691
+ // const prop = objectLiteralExpr.getProperty(propName);
692
+ // if (prop && prop.getKind() === SyntaxKind.PropertyAssignment) {
693
+ // const elements = prop.getFirstDescendantByKind(SyntaxKind.ArrayLiteralExpression);
694
+ // elements?.getElements().forEach(el => {
695
+ // const text = el.getText();
696
+ // if (filesToDelete.some(file => text.toLowerCase().includes(file.split('.')[0]))) {
697
+ // // @ts-ignore
698
+ // el.remove();
699
+ // }
700
+ // });
701
+ // }
702
+ // }
703
+ // }
704
+
705
+ // // Save changes
706
+ // sourceFile.saveSync();
707
+
708
+ // Run seeder to reflect the removal.
709
+
710
+ // - | Drop database table | Removes the database table from the DB, this is a very risky step. Best to review all relations to other models etc and then do this manually | Manual (X)
711
+
712
+ }
713
+
714
+ async handleGenerateCode(options: CodeGenerationOptions): Promise<any> {
715
+ // // see if the model record exists.
716
+ // const model = await this.modelMetadataRepo.findOne({
717
+ // where: {
718
+ // id: options.modelId,
719
+ // },
720
+ // });
721
+ // if (!model) {
722
+ // throw new NotFoundException(`Model record with #${options.modelId} not found`);
723
+ // }
724
+
725
+ // const m = {
726
+ // payload: options,
727
+ // parentEntity: model.singularName,
728
+ // parentEntityId: options.modelId,
729
+ // };
730
+
731
+ // const messageId = await this.generateCodePublihser.publish(m);
732
+
733
+ // return { messageId: messageId };
734
+
519
735
  const { model, removeFieldCodeOuput, refreshModelCodeOutput } = await this.generateCode(options);
520
736
 
521
737
  // Generate the code for models which are linked to fields having an inverse relation
@@ -531,14 +747,13 @@ export class ModelMetadataService {
531
747
  };
532
748
  await this.generateCode(inverseOptions);
533
749
  }
534
-
535
750
  await this.generateVAMConfig(model.id);
536
751
 
537
752
  return `${removeFieldCodeOuput} \n ${refreshModelCodeOutput}`;
538
753
  }
539
754
 
540
755
  // Generate the View, Action and Menu configuration for the model
541
- private async generateVAMConfig(modelId: number) {
756
+ async generateVAMConfig(modelId: number) {
542
757
  try {
543
758
  return await this.dataSource.transaction(async (manager: EntityManager) => {
544
759
  const modelRepository = manager.getRepository(ModelMetadata);
@@ -852,7 +1067,7 @@ export class ModelMetadataService {
852
1067
  }
853
1068
  }
854
1069
 
855
- private async generateCode(options: CodeGenerationOptions) {
1070
+ async generateCode(options: CodeGenerationOptions) {
856
1071
  const query = {
857
1072
  populate: ["module", "fields"]
858
1073
  };
@@ -894,6 +1109,34 @@ export class ModelMetadataService {
894
1109
  this.fieldMetadataService.delete(field.id);
895
1110
  }
896
1111
  });
1112
+
1113
+ // Remove the fields from metadata json file
1114
+
1115
+ const filePath = await this.moduleMetadataHelperService.getModuleMetadataFilePath(model.module.name);
1116
+ const metaData = await this.moduleMetadataHelperService.getModuleMetadataConfiguration(filePath);
1117
+
1118
+ // Check if the model already exists in `models`
1119
+ const existingModelIndex = metaData.moduleMetadata.models.findIndex(
1120
+ (existingModel: any) => existingModel.singularName === model.singularName
1121
+ );
1122
+
1123
+ const modelMetaData = metaData.moduleMetadata.models[existingModelIndex];
1124
+
1125
+ // Remove fields marked for removal from modelMetaData.fields
1126
+ modelMetaData.fields = modelMetaData.fields.filter((field: any) => field.isMarkedForRemoval !== true);
1127
+
1128
+ if (existingModelIndex !== -1) {
1129
+ // Update the existing model
1130
+ metaData.moduleMetadata.models[existingModelIndex] = modelMetaData;
1131
+ } else {
1132
+ // Add the new model
1133
+ metaData.moduleMetadata.models.push(modelMetaData);
1134
+ }
1135
+
1136
+ // Write the updated object back to the file
1137
+ const updatedContent = JSON.stringify(metaData, null, 2);
1138
+ await fs.writeFile(filePath, updatedContent);
1139
+
897
1140
  return removeOutput;
898
1141
  }
899
1142
 
@@ -1,4 +1,4 @@
1
- import { Injectable } from '@nestjs/common';
1
+ import { forwardRef, Inject, Injectable } from '@nestjs/common';
2
2
  import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
3
3
  import { DiscoveryService, ModuleRef } from "@nestjs/core";
4
4
  import { EntityManager, Repository } from 'typeorm';
@@ -15,6 +15,7 @@ import { MqMessageQueue } from '../entities/mq-message-queue.entity';
15
15
  @Injectable()
16
16
  export class MqMessageQueueService extends CRUDService<MqMessageQueue> {
17
17
  constructor(
18
+ @Inject(forwardRef(() => ModelMetadataService))
18
19
  readonly modelMetadataService: ModelMetadataService,
19
20
  readonly moduleMetadataService: ModuleMetadataService,
20
21
  readonly configService: ConfigService,
@@ -1,4 +1,4 @@
1
- import { Injectable } from '@nestjs/common';
1
+ import { forwardRef, Inject, Injectable } from '@nestjs/common';
2
2
  import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
3
3
  import { DiscoveryService, ModuleRef } from "@nestjs/core";
4
4
  import { EntityManager, Repository } from 'typeorm';
@@ -8,25 +8,64 @@ import { ModuleMetadataService } from 'src/services/module-metadata.service';
8
8
  import { ConfigService } from '@nestjs/config';
9
9
  import { FileService } from "src/services/file.service";
10
10
  import { CrudHelperService } from "src/services/crud-helper.service";
11
-
12
-
13
11
  import { MqMessage } from '../entities/mq-message.entity';
12
+ import { Logger } from '@nestjs/common';
14
13
 
15
14
  @Injectable()
16
- export class MqMessageService extends CRUDService<MqMessage>{
15
+ export class MqMessageService extends CRUDService<MqMessage> {
16
+ private readonly logger = new Logger(MqMessageService.name);
17
+
17
18
  constructor(
19
+ @Inject(forwardRef(() => ModelMetadataService))
18
20
  readonly modelMetadataService: ModelMetadataService,
19
21
  readonly moduleMetadataService: ModuleMetadataService,
20
22
  readonly configService: ConfigService,
21
23
  readonly fileService: FileService,
22
- readonly discoveryService: DiscoveryService,
24
+ readonly discoveryService: DiscoveryService,
23
25
  readonly crudHelperService: CrudHelperService,
24
26
  @InjectEntityManager()
25
27
  readonly entityManager: EntityManager,
26
28
  @InjectRepository(MqMessage)
27
29
  readonly repo: Repository<MqMessage>,
28
- readonly moduleRef: ModuleRef
30
+ readonly moduleRef: ModuleRef
29
31
  ) {
30
- super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService,entityManager, repo, 'mqMessage', 'queues',moduleRef);
31
- }
32
+ super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService, entityManager, repo, 'mqMessage', 'queues', moduleRef);
33
+ }
34
+
35
+ async lockNextPendingMessage(queueName: string): Promise<MqMessage | null> {
36
+ // this.logger.debug(`Locking next pending message for queue: ${queueName}`);
37
+
38
+ return await this.entityManager.transaction(async manager => {
39
+ // Use raw SQL to skip locked rows (FOR UPDATE SKIP LOCKED)
40
+ const rawJob = await manager.query(`
41
+ SELECT ss_mq_message.id
42
+ FROM ss_mq_message
43
+ left join ss_mq_message_queue on ss_mq_message.mq_message_queue_id = ss_mq_message_queue.id
44
+ WHERE ss_mq_message_queue."name" = $1
45
+ AND ss_mq_message.stage = 'pending'
46
+ ORDER BY ss_mq_message.created_at ASC
47
+ FOR UPDATE SKIP LOCKED
48
+ LIMIT 1`,
49
+ [queueName]
50
+ );
51
+
52
+ // this.logger.debug(`Raw job fetched: ${JSON.stringify(rawJob)}`);
53
+ if (!rawJob || rawJob.length === 0) {
54
+ // this.logger.debug(`No pending job found for queue: ${queueName}`);
55
+ return null;
56
+ }
57
+ const job = await manager.getRepository(MqMessage).findOne({ where: { id: rawJob[0].id }, relations: ['mqMessageQueue'] });
58
+ if (job) {
59
+ // this.logger.debug(`Locked job id: ${job.id}, queue: ${job.mqMessageQueue.name}, stage: ${job.stage}`);
60
+
61
+ job.stage = 'scheduled';
62
+ await manager.save(job);
63
+
64
+ return job;
65
+ }
66
+
67
+ return null;
68
+ });
69
+ }
70
+
32
71
  }
@@ -0,0 +1,79 @@
1
+ import { Logger } from '@nestjs/common';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+ import { QueuesModuleOptions } from "../../interfaces";
4
+ import { QueueMessage, QueuePublisher } from '../../interfaces/mq';
5
+ import { MqMessageQueueService } from '../mq-message-queue.service';
6
+ import { MqMessageService } from '../mq-message.service';
7
+
8
+ export abstract class DatabasePublisher<T> implements QueuePublisher<T> {
9
+ private readonly logger = new Logger(DatabasePublisher.name);
10
+ private readonly url: string;
11
+ private readonly serviceRole: string;
12
+
13
+ constructor(
14
+ protected readonly mqMessageService: MqMessageService,
15
+ protected readonly mqMessageQueueService: MqMessageQueueService,
16
+ ) {
17
+ this.serviceRole = process.env.QUEUES_SERVICE_ROLE;
18
+ if (!this.serviceRole) {
19
+ this.logger.debug('Queue service Role is not defined in the environment variables');
20
+ }
21
+ this.logger.debug(`DatabasePublisher instance created with options: ${JSON.stringify(this.options())} and url: ${this.url}`);
22
+ }
23
+
24
+ abstract options(): QueuesModuleOptions;
25
+
26
+ async publish(message: QueueMessage<T>): Promise<string> {
27
+ if (!this.serviceRole) {
28
+ this.logger.error('Queue service Role is not defined in the environment variables');
29
+ throw new Error('Queue service Role is not defined in the environment variables');
30
+ }
31
+ if (this.serviceRole === 'subscriber') {
32
+ this.logger.error('Queue service Role is subscriber, cannot publish messages');
33
+ throw new Error('Queue service Role is subscriber, cannot publish messages');
34
+ }
35
+
36
+ this.logger.debug(`DatabasePublisher publishing with options: ${JSON.stringify(this.options())} and url: ${this.url}`);
37
+
38
+ const options = this.options();
39
+
40
+ const queueName = options.queueName;
41
+ if (!message.retryCount) message.retryCount = 0;
42
+ if (!message.retryInterval) message.retryInterval = 1000;
43
+
44
+ // generate a new message id
45
+ message.messageId = uuidv4();
46
+
47
+ // Save the message to the DB so that we can then change its status in the subscriber...
48
+ await this.persistToDatabase(queueName, message);
49
+
50
+ // return the newly created message id.
51
+ return message.messageId;
52
+ }
53
+
54
+ private async persistToDatabase(queueName: string, message: QueueMessage<T>) {
55
+ // make an entry in the relevant database table, generate a unique id earlier.
56
+ try {
57
+ // 1. resolve the queue first
58
+ const mqMessageQueue = await this.mqMessageQueueService.resolveQueue(queueName);
59
+
60
+ // 2. Next create an entry in the mqMessage table.
61
+ await this.mqMessageService.create({
62
+ messageBroker: this.options().type,
63
+ messageId: message.messageId,
64
+ retryCount: message.retryCount,
65
+ retryInterval: message.retryInterval,
66
+ stage: 'pending',
67
+ startedAt: new Date(),
68
+ input: JSON.stringify(message, null, 2),
69
+ parentEntityId: message.parentEntityId,
70
+ parentEntity: message.parentEntity,
71
+ mqMessageQueueId: mqMessageQueue.id,
72
+ });
73
+ }
74
+ catch (error) {
75
+ this.logger.error(error.message, error.stack);
76
+ }
77
+
78
+ }
79
+ }