@solidxai/core 0.1.6-beta.9 → 0.1.7

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 (311) hide show
  1. package/.claude/settings.local.json +15 -0
  2. package/CHANGELOG.md +71 -0
  3. package/dist/controllers/dashboard-layout.controller.d.ts +47 -0
  4. package/dist/controllers/dashboard-layout.controller.d.ts.map +1 -0
  5. package/dist/controllers/dashboard-layout.controller.js +204 -0
  6. package/dist/controllers/dashboard-layout.controller.js.map +1 -0
  7. package/dist/dtos/create-dashboard-layout.dto.d.ts +8 -0
  8. package/dist/dtos/create-dashboard-layout.dto.d.ts.map +1 -0
  9. package/dist/dtos/create-dashboard-layout.dto.js +53 -0
  10. package/dist/dtos/create-dashboard-layout.dto.js.map +1 -0
  11. package/dist/dtos/create-dashboard-variable.dto.d.ts +1 -0
  12. package/dist/dtos/create-dashboard-variable.dto.d.ts.map +1 -1
  13. package/dist/dtos/create-dashboard-variable.dto.js +7 -1
  14. package/dist/dtos/create-dashboard-variable.dto.js.map +1 -1
  15. package/dist/dtos/update-dashboard-layout.dto.d.ts +8 -0
  16. package/dist/dtos/update-dashboard-layout.dto.d.ts.map +1 -0
  17. package/dist/dtos/update-dashboard-layout.dto.js +53 -0
  18. package/dist/dtos/update-dashboard-layout.dto.js.map +1 -0
  19. package/dist/dtos/update-dashboard-variable.dto.d.ts +1 -0
  20. package/dist/dtos/update-dashboard-variable.dto.d.ts.map +1 -1
  21. package/dist/dtos/update-dashboard-variable.dto.js +7 -1
  22. package/dist/dtos/update-dashboard-variable.dto.js.map +1 -1
  23. package/dist/entities/action-metadata.entity.d.ts.map +1 -1
  24. package/dist/entities/action-metadata.entity.js.map +1 -1
  25. package/dist/entities/ai-interaction.entity.d.ts.map +1 -1
  26. package/dist/entities/ai-interaction.entity.js +5 -4
  27. package/dist/entities/ai-interaction.entity.js.map +1 -1
  28. package/dist/entities/chatter-message-details.entity.d.ts.map +1 -1
  29. package/dist/entities/chatter-message-details.entity.js +4 -3
  30. package/dist/entities/chatter-message-details.entity.js.map +1 -1
  31. package/dist/entities/chatter-message.entity.d.ts.map +1 -1
  32. package/dist/entities/chatter-message.entity.js +4 -3
  33. package/dist/entities/chatter-message.entity.js.map +1 -1
  34. package/dist/entities/dashboard-layout.entity.d.ts +9 -0
  35. package/dist/entities/dashboard-layout.entity.d.ts.map +1 -0
  36. package/dist/entities/dashboard-layout.entity.js +41 -0
  37. package/dist/entities/dashboard-layout.entity.js.map +1 -0
  38. package/dist/entities/dashboard-question-sql-dataset-config.entity.d.ts.map +1 -1
  39. package/dist/entities/dashboard-question-sql-dataset-config.entity.js +5 -4
  40. package/dist/entities/dashboard-question-sql-dataset-config.entity.js.map +1 -1
  41. package/dist/entities/dashboard-question.entity.d.ts.map +1 -1
  42. package/dist/entities/dashboard-question.entity.js +5 -4
  43. package/dist/entities/dashboard-question.entity.js.map +1 -1
  44. package/dist/entities/dashboard-variable.entity.d.ts +1 -0
  45. package/dist/entities/dashboard-variable.entity.d.ts.map +1 -1
  46. package/dist/entities/dashboard-variable.entity.js +10 -4
  47. package/dist/entities/dashboard-variable.entity.js.map +1 -1
  48. package/dist/entities/dashboard.entity.d.ts +2 -0
  49. package/dist/entities/dashboard.entity.d.ts.map +1 -1
  50. package/dist/entities/dashboard.entity.js +9 -3
  51. package/dist/entities/dashboard.entity.js.map +1 -1
  52. package/dist/entities/email-attachment.entity.d.ts.map +1 -1
  53. package/dist/entities/email-attachment.entity.js +2 -1
  54. package/dist/entities/email-attachment.entity.js.map +1 -1
  55. package/dist/entities/email-template.entity.js +1 -1
  56. package/dist/entities/email-template.entity.js.map +1 -1
  57. package/dist/entities/export-transaction.entity.d.ts.map +1 -1
  58. package/dist/entities/export-transaction.entity.js +2 -1
  59. package/dist/entities/export-transaction.entity.js.map +1 -1
  60. package/dist/entities/field-metadata.entity.js +2 -2
  61. package/dist/entities/field-metadata.entity.js.map +1 -1
  62. package/dist/entities/import-transaction-error-log.entity.d.ts.map +1 -1
  63. package/dist/entities/import-transaction-error-log.entity.js +3 -2
  64. package/dist/entities/import-transaction-error-log.entity.js.map +1 -1
  65. package/dist/entities/import-transaction.entity.d.ts.map +1 -1
  66. package/dist/entities/import-transaction.entity.js +2 -1
  67. package/dist/entities/import-transaction.entity.js.map +1 -1
  68. package/dist/entities/mq-message-queue.entity.d.ts.map +1 -1
  69. package/dist/entities/mq-message-queue.entity.js.map +1 -1
  70. package/dist/entities/mq-message.entity.d.ts.map +1 -1
  71. package/dist/entities/mq-message.entity.js +5 -3
  72. package/dist/entities/mq-message.entity.js.map +1 -1
  73. package/dist/entities/saved-filters.entity.d.ts.map +1 -1
  74. package/dist/entities/saved-filters.entity.js +3 -2
  75. package/dist/entities/saved-filters.entity.js.map +1 -1
  76. package/dist/entities/security-rule.entity.d.ts.map +1 -1
  77. package/dist/entities/security-rule.entity.js +2 -1
  78. package/dist/entities/security-rule.entity.js.map +1 -1
  79. package/dist/entities/sms-template.entity.js +1 -1
  80. package/dist/entities/sms-template.entity.js.map +1 -1
  81. package/dist/entities/user-view-metadata.entity.d.ts.map +1 -1
  82. package/dist/entities/user-view-metadata.entity.js +2 -1
  83. package/dist/entities/user-view-metadata.entity.js.map +1 -1
  84. package/dist/entities/user.entity.d.ts.map +1 -1
  85. package/dist/entities/user.entity.js +2 -0
  86. package/dist/entities/user.entity.js.map +1 -1
  87. package/dist/entities/view-metadata.entity.d.ts.map +1 -1
  88. package/dist/entities/view-metadata.entity.js.map +1 -1
  89. package/dist/helpers/bootstrap.helper.d.ts +14 -0
  90. package/dist/helpers/bootstrap.helper.d.ts.map +1 -0
  91. package/dist/helpers/bootstrap.helper.js +132 -0
  92. package/dist/helpers/bootstrap.helper.js.map +1 -0
  93. package/dist/helpers/cache.helper.d.ts +2 -0
  94. package/dist/helpers/cache.helper.d.ts.map +1 -0
  95. package/dist/helpers/cache.helper.js +8 -0
  96. package/dist/helpers/cache.helper.js.map +1 -0
  97. package/dist/helpers/cors.helper.d.ts.map +1 -1
  98. package/dist/helpers/cors.helper.js +13 -4
  99. package/dist/helpers/cors.helper.js.map +1 -1
  100. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.d.ts +1 -0
  101. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.d.ts.map +1 -1
  102. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js +8 -9
  103. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js.map +1 -1
  104. package/dist/helpers/field-crud-managers/SelectionDynamicFieldCrudManager.d.ts +2 -2
  105. package/dist/helpers/field-crud-managers/SelectionDynamicFieldCrudManager.d.ts.map +1 -1
  106. package/dist/helpers/field-crud-managers/SelectionDynamicFieldCrudManager.js +8 -5
  107. package/dist/helpers/field-crud-managers/SelectionDynamicFieldCrudManager.js.map +1 -1
  108. package/dist/helpers/solid-registry.d.ts +3 -0
  109. package/dist/helpers/solid-registry.d.ts.map +1 -1
  110. package/dist/helpers/solid-registry.js +7 -0
  111. package/dist/helpers/solid-registry.js.map +1 -1
  112. package/dist/helpers/typeorm-db-helper.d.ts.map +1 -1
  113. package/dist/helpers/typeorm-db-helper.js +21 -0
  114. package/dist/helpers/typeorm-db-helper.js.map +1 -1
  115. package/dist/index.d.ts +3 -0
  116. package/dist/index.d.ts.map +1 -1
  117. package/dist/index.js +3 -0
  118. package/dist/index.js.map +1 -1
  119. package/dist/interfaces.d.ts +2 -0
  120. package/dist/interfaces.d.ts.map +1 -1
  121. package/dist/interfaces.js.map +1 -1
  122. package/dist/jobs/chatter-queue-options.js +1 -1
  123. package/dist/jobs/chatter-queue-options.js.map +1 -1
  124. package/dist/jobs/chatter-queue-publisher.service.d.ts +9 -9
  125. package/dist/jobs/chatter-queue-publisher.service.d.ts.map +1 -1
  126. package/dist/jobs/chatter-queue-publisher.service.js +5 -5
  127. package/dist/jobs/chatter-queue-publisher.service.js.map +1 -1
  128. package/dist/jobs/chatter-queue-subscriber.service.d.ts +4 -4
  129. package/dist/jobs/chatter-queue-subscriber.service.d.ts.map +1 -1
  130. package/dist/jobs/chatter-queue-subscriber.service.js +11 -11
  131. package/dist/jobs/chatter-queue-subscriber.service.js.map +1 -1
  132. package/dist/jobs/computed-field-evaluation-queue-options.d.ts +2 -0
  133. package/dist/jobs/computed-field-evaluation-queue-options.d.ts.map +1 -1
  134. package/dist/jobs/computed-field-evaluation-queue-options.js +2 -0
  135. package/dist/jobs/computed-field-evaluation-queue-options.js.map +1 -1
  136. package/dist/jobs/database/chatter-queue-options-database.d.ts +8 -0
  137. package/dist/jobs/database/chatter-queue-options-database.d.ts.map +1 -0
  138. package/dist/jobs/database/chatter-queue-options-database.js +10 -0
  139. package/dist/jobs/database/chatter-queue-options-database.js.map +1 -0
  140. package/dist/jobs/database/chatter-queue-publisher-database.service.d.ts +12 -0
  141. package/dist/jobs/database/chatter-queue-publisher-database.service.d.ts.map +1 -0
  142. package/dist/jobs/database/chatter-queue-publisher-database.service.js +39 -0
  143. package/dist/jobs/database/chatter-queue-publisher-database.service.js.map +1 -0
  144. package/dist/jobs/database/chatter-queue-subscriber-database.service.d.ts +19 -0
  145. package/dist/jobs/database/chatter-queue-subscriber-database.service.d.ts.map +1 -0
  146. package/dist/jobs/database/chatter-queue-subscriber-database.service.js +62 -0
  147. package/dist/jobs/database/chatter-queue-subscriber-database.service.js.map +1 -0
  148. package/dist/repository/dashboard-layout.repository.d.ts +12 -0
  149. package/dist/repository/dashboard-layout.repository.d.ts.map +1 -0
  150. package/dist/repository/dashboard-layout.repository.js +34 -0
  151. package/dist/repository/dashboard-layout.repository.js.map +1 -0
  152. package/dist/repository/model-metadata.repository.d.ts +6 -1
  153. package/dist/repository/model-metadata.repository.d.ts.map +1 -1
  154. package/dist/repository/model-metadata.repository.js +41 -2
  155. package/dist/repository/model-metadata.repository.js.map +1 -1
  156. package/dist/seeders/module-metadata-seeder.service.js +4 -4
  157. package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
  158. package/dist/seeders/seed-data/solid-core-metadata.json +372 -32
  159. package/dist/services/chatter-message.service.d.ts +4 -4
  160. package/dist/services/chatter-message.service.d.ts.map +1 -1
  161. package/dist/services/chatter-message.service.js +33 -9
  162. package/dist/services/chatter-message.service.js.map +1 -1
  163. package/dist/services/computed-fields/entity/sequence-num-computed-field-provider.d.ts +7 -3
  164. package/dist/services/computed-fields/entity/sequence-num-computed-field-provider.d.ts.map +1 -1
  165. package/dist/services/computed-fields/entity/sequence-num-computed-field-provider.js +61 -22
  166. package/dist/services/computed-fields/entity/sequence-num-computed-field-provider.js.map +1 -1
  167. package/dist/services/crud.service.js +1 -1
  168. package/dist/services/crud.service.js.map +1 -1
  169. package/dist/services/dashboard-layout.service.d.ts +20 -0
  170. package/dist/services/dashboard-layout.service.d.ts.map +1 -0
  171. package/dist/services/dashboard-layout.service.js +120 -0
  172. package/dist/services/dashboard-layout.service.js.map +1 -0
  173. package/dist/services/dashboard.service.d.ts +2 -0
  174. package/dist/services/dashboard.service.d.ts.map +1 -1
  175. package/dist/services/dashboard.service.js +4 -0
  176. package/dist/services/dashboard.service.js.map +1 -1
  177. package/dist/services/model-metadata.service.d.ts +3 -1
  178. package/dist/services/model-metadata.service.d.ts.map +1 -1
  179. package/dist/services/model-metadata.service.js +21 -2
  180. package/dist/services/model-metadata.service.js.map +1 -1
  181. package/dist/services/permission-metadata.service.d.ts +5 -1
  182. package/dist/services/permission-metadata.service.d.ts.map +1 -1
  183. package/dist/services/permission-metadata.service.js +66 -20
  184. package/dist/services/permission-metadata.service.js.map +1 -1
  185. package/dist/services/queues/database-subscriber.service.d.ts.map +1 -1
  186. package/dist/services/queues/database-subscriber.service.js +6 -1
  187. package/dist/services/queues/database-subscriber.service.js.map +1 -1
  188. package/dist/services/queues/publisher-factory.service.js +0 -1
  189. package/dist/services/queues/publisher-factory.service.js.map +1 -1
  190. package/dist/services/queues/rabbitmq-publisher.service.d.ts +1 -0
  191. package/dist/services/queues/rabbitmq-publisher.service.d.ts.map +1 -1
  192. package/dist/services/queues/rabbitmq-publisher.service.js +6 -1
  193. package/dist/services/queues/rabbitmq-publisher.service.js.map +1 -1
  194. package/dist/services/queues/rabbitmq-subscriber.service.d.ts +5 -1
  195. package/dist/services/queues/rabbitmq-subscriber.service.d.ts.map +1 -1
  196. package/dist/services/queues/rabbitmq-subscriber.service.js +84 -9
  197. package/dist/services/queues/rabbitmq-subscriber.service.js.map +1 -1
  198. package/dist/services/request-context.service.d.ts +2 -1
  199. package/dist/services/request-context.service.d.ts.map +1 -1
  200. package/dist/services/request-context.service.js.map +1 -1
  201. package/dist/services/scheduled-jobs/scheduler.service.d.ts.map +1 -1
  202. package/dist/services/scheduled-jobs/scheduler.service.js +20 -2
  203. package/dist/services/scheduled-jobs/scheduler.service.js.map +1 -1
  204. package/dist/services/solid-introspect.service.d.ts +6 -1
  205. package/dist/services/solid-introspect.service.d.ts.map +1 -1
  206. package/dist/services/solid-introspect.service.js +27 -2
  207. package/dist/services/solid-introspect.service.js.map +1 -1
  208. package/dist/services/solid-ts-morph.service.d.ts +9 -0
  209. package/dist/services/solid-ts-morph.service.d.ts.map +1 -1
  210. package/dist/services/solid-ts-morph.service.js +76 -0
  211. package/dist/services/solid-ts-morph.service.js.map +1 -1
  212. package/dist/solid-core.module.d.ts.map +1 -1
  213. package/dist/solid-core.module.js +16 -0
  214. package/dist/solid-core.module.js.map +1 -1
  215. package/dist/subscribers/audit.subscriber.d.ts +10 -7
  216. package/dist/subscribers/audit.subscriber.d.ts.map +1 -1
  217. package/dist/subscribers/audit.subscriber.js +58 -85
  218. package/dist/subscribers/audit.subscriber.js.map +1 -1
  219. package/dist/subscribers/computed-entity-field.subscriber.js +3 -1
  220. package/dist/subscribers/computed-entity-field.subscriber.js.map +1 -1
  221. package/dist/subscribers/created-by-updated-by.subscriber.d.ts +0 -1
  222. package/dist/subscribers/created-by-updated-by.subscriber.d.ts.map +1 -1
  223. package/dist/subscribers/created-by-updated-by.subscriber.js +3 -13
  224. package/dist/subscribers/created-by-updated-by.subscriber.js.map +1 -1
  225. package/dist/winston.logger.d.ts.map +1 -1
  226. package/dist/winston.logger.js +2 -1
  227. package/dist/winston.logger.js.map +1 -1
  228. package/package.json +3 -1
  229. package/sql/default/mariadb/proc_CleanupModelMetadata.sql +153 -0
  230. package/sql/default/mariadb/proc_CleanupModuleMetadata.sql +56 -0
  231. package/sql/default/mysql/proc_CleanupModelMetadata.sql +153 -0
  232. package/sql/default/mysql/proc_CleanupModuleMetadata.sql +56 -0
  233. package/src/controllers/dashboard-layout.controller.ts +106 -0
  234. package/src/dtos/create-dashboard-layout.dto.ts +31 -0
  235. package/src/dtos/create-dashboard-variable.dto.ts +4 -0
  236. package/src/dtos/update-dashboard-layout.dto.ts +30 -0
  237. package/src/dtos/update-dashboard-variable.dto.ts +5 -1
  238. package/src/entities/action-metadata.entity.ts +3 -2
  239. package/src/entities/ai-interaction.entity.ts +5 -4
  240. package/src/entities/chatter-message-details.entity.ts +4 -3
  241. package/src/entities/chatter-message.entity.ts +4 -3
  242. package/src/entities/dashboard-layout.entity.ts +18 -0
  243. package/src/entities/dashboard-question-sql-dataset-config.entity.ts +5 -4
  244. package/src/entities/dashboard-question.entity.ts +5 -4
  245. package/src/entities/dashboard-variable.entity.ts +9 -4
  246. package/src/entities/dashboard.entity.ts +7 -2
  247. package/src/entities/email-attachment.entity.ts +2 -1
  248. package/src/entities/email-template.entity.ts +1 -1
  249. package/src/entities/export-transaction.entity.ts +2 -1
  250. package/src/entities/field-metadata.entity.ts +2 -2
  251. package/src/entities/import-transaction-error-log.entity.ts +3 -2
  252. package/src/entities/import-transaction.entity.ts +2 -1
  253. package/src/entities/mq-message-queue.entity.ts +8 -8
  254. package/src/entities/mq-message.entity.ts +5 -3
  255. package/src/entities/saved-filters.entity.ts +3 -2
  256. package/src/entities/security-rule.entity.ts +2 -1
  257. package/src/entities/sms-template.entity.ts +1 -1
  258. package/src/entities/user-view-metadata.entity.ts +2 -1
  259. package/src/entities/user.entity.ts +37 -2
  260. package/src/entities/view-metadata.entity.ts +3 -0
  261. package/src/helpers/bootstrap.helper.ts +222 -0
  262. package/src/helpers/cache.helper.ts +5 -0
  263. package/src/helpers/cors.helper.ts +26 -6
  264. package/src/helpers/field-crud-managers/MediaFieldCrudManager.ts +9 -9
  265. package/src/helpers/field-crud-managers/SelectionDynamicFieldCrudManager.ts +9 -6
  266. package/src/helpers/solid-registry.ts +10 -5
  267. package/src/helpers/typeorm-db-helper.ts +26 -0
  268. package/src/index.ts +3 -0
  269. package/src/interfaces.ts +3 -0
  270. package/src/jobs/chatter-queue-options.ts +1 -1
  271. package/src/jobs/chatter-queue-publisher.service.ts +11 -11
  272. package/src/jobs/chatter-queue-subscriber.service.ts +13 -8
  273. package/src/jobs/computed-field-evaluation-queue-options.ts +2 -0
  274. package/src/jobs/database/chatter-queue-options-database.ts +9 -0
  275. package/src/jobs/database/chatter-queue-publisher-database.service.ts +24 -0
  276. package/src/jobs/database/chatter-queue-subscriber-database.service.ts +53 -0
  277. package/src/repository/dashboard-layout.repository.ts +17 -0
  278. package/src/repository/model-metadata.repository.ts +45 -2
  279. package/src/seeders/module-metadata-seeder.service.ts +5 -5
  280. package/src/seeders/seed-data/solid-core-metadata.json +373 -33
  281. package/src/services/1.js +6 -0
  282. package/src/services/chatter-message.service.ts +41 -9
  283. package/src/services/computed-fields/entity/sequence-num-computed-field-provider.ts +79 -40
  284. package/src/services/crud.service.ts +1 -1
  285. package/src/services/dashboard-layout.service.ts +111 -0
  286. package/src/services/dashboard.service.ts +7 -0
  287. package/src/services/model-metadata.service.ts +22 -43
  288. package/src/services/permission-metadata.service.ts +73 -20
  289. package/src/services/queues/database-subscriber.service.ts +7 -1
  290. package/src/services/queues/publisher-factory.service.ts +1 -1
  291. package/src/services/queues/rabbitmq-publisher.service.ts +8 -2
  292. package/src/services/queues/rabbitmq-subscriber.service.ts +127 -10
  293. package/src/services/request-context.service.ts +2 -1
  294. package/src/services/scheduled-jobs/scheduler.service.ts +22 -4
  295. package/src/services/solid-introspect.service.ts +28 -0
  296. package/src/services/solid-ts-morph.service.ts +98 -0
  297. package/src/solid-core.module.ts +21 -2
  298. package/src/subscribers/audit.subscriber.ts +63 -271
  299. package/src/subscribers/computed-entity-field.subscriber.ts +3 -3
  300. package/src/subscribers/created-by-updated-by.subscriber.ts +22 -16
  301. package/src/winston.logger.ts +2 -1
  302. package/dist-tests/api/authenticate.spec.js +0 -119
  303. package/dist-tests/api/authenticate.spec.js.map +0 -1
  304. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js +0 -97
  305. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js.map +0 -1
  306. package/dist-tests/api/ping.spec.js +0 -21
  307. package/dist-tests/api/ping.spec.js.map +0 -1
  308. package/dist-tests/helpers/auth.js +0 -41
  309. package/dist-tests/helpers/auth.js.map +0 -1
  310. package/dist-tests/helpers/env.js +0 -11
  311. package/dist-tests/helpers/env.js.map +0 -1
@@ -1,32 +1,20 @@
1
- import { forwardRef, Inject, Injectable, Scope } from '@nestjs/common';
2
- import { ModelMetadataHelperService } from 'src/helpers/model-metadata-helper.service';
1
+ import { Injectable, Logger, Scope } from '@nestjs/common';
3
2
  import { lowerFirst } from 'src/helpers/string.helper';
4
- import { ModelMetadataRepository } from 'src/repository/model-metadata.repository';
3
+ import { SolidRegistry } from 'src/helpers/solid-registry';
5
4
  import { DataSource, EntityMetadata, EntitySubscriberInterface, InsertEvent, RemoveEvent, UpdateEvent } from 'typeorm';
6
- import { ChatterMessageService } from '../services/chatter-message.service';
7
-
8
-
9
- type DeferredCall =
10
- | { kind: 'insert'; args: Parameters<ChatterMessageService['postAuditMessageOnInsert']> }
11
- | { kind: 'update'; args: Parameters<ChatterMessageService['postAuditMessageOnUpdate']> }
12
- | { kind: 'delete'; args: Parameters<ChatterMessageService['postAuditMessageOnDelete']> };
5
+ import { AuditQueuePayload } from 'src/jobs/chatter-queue-publisher.service';
6
+ import { RequestContextService } from 'src/services/request-context.service';
7
+ import { PublisherFactory } from 'src/services/queues/publisher-factory.service';
13
8
 
14
9
  @Injectable({scope: Scope.TRANSIENT})
15
- // @EventSubscriber()
16
10
  export class AuditSubscriber implements EntitySubscriberInterface {
11
+ private readonly logger = new Logger(AuditSubscriber.name);
17
12
  private dataSource: DataSource;
18
13
  constructor(
19
- // @InjectDataSource()
20
- // private readonly dataSource: DataSource,
21
- private readonly chatterMessageService: ChatterMessageService,
22
- // @InjectRepository(ModelMetadata)
23
- // private readonly modelMetadataRepo: Repository<ModelMetadata>,
24
- @Inject(forwardRef(() => ModelMetadataRepository))
25
- private readonly modelMetadataRepo: ModelMetadataRepository,
26
- private readonly modelMetadataHelperService: ModelMetadataHelperService,
27
- ) {
28
- // this.dataSource.subscribers.push(this);
29
- }
14
+ private readonly publisherFactory: PublisherFactory<AuditQueuePayload>,
15
+ private readonly solidRegistry: SolidRegistry,
16
+ private readonly requestContextService: RequestContextService,
17
+ ) { }
30
18
 
31
19
  bindToDataSource(dataSource: DataSource) {
32
20
  this.dataSource = dataSource;
@@ -34,90 +22,61 @@ export class AuditSubscriber implements EntitySubscriberInterface {
34
22
  }
35
23
 
36
24
  // Per-transaction buffer (auto-GC when queryRunner is gone)
37
- private perTxn = new WeakMap<any, DeferredCall[]>();
25
+ private perTxn = new WeakMap<any, AuditQueuePayload[]>();
38
26
 
39
- private enqueue(event: { queryRunner: any }, call: DeferredCall) {
27
+ private enqueue(event: { queryRunner: any }, payload: AuditQueuePayload) {
40
28
  const qr = event.queryRunner;
41
29
  const arr = this.perTxn.get(qr) ?? [];
42
- arr.push(call);
30
+ arr.push(payload);
43
31
  this.perTxn.set(qr, arr);
44
32
  }
45
33
 
46
- private async shouldTrackAudit(entity: any, metadata: EntityMetadata): Promise<boolean> {
47
- const model = await this.modelMetadataRepo.findOne({
48
- where: {
49
- singularName: lowerFirst(metadata.name)
50
- },
51
- relations: {
52
- fields: true,
53
- module: true
54
- }
55
- });
56
-
57
- if (!model || !model.enableAuditTracking) {
58
- return false;
59
- }
60
-
61
- const modelFields = await this.modelMetadataHelperService.loadFieldHierarchy(model.singularName)
62
-
63
- const auditFields = modelFields.filter(field =>
64
- field.enableAuditTracking &&
65
- !['mediaSingle', 'mediaMultiple', 'richText', 'json'].includes(field.type) &&
66
- !(field.type === 'relation' && field.relationType === 'one-to-many')
67
- );
68
-
69
- if (auditFields.length === 0) {
70
- return false;
71
- }
72
-
73
- // if (!entity) {
74
- // console.warn(`[AuditSubscriber] Skipping audit for ${metadata.name} – entity is undefined or null`);
75
- // return false;
76
- // }
34
+ private shouldTrackAudit(metadata: EntityMetadata): boolean {
35
+ return this.solidRegistry.isAuditableModel(lowerFirst(metadata.name));
36
+ }
77
37
 
78
- return entity && auditFields.some(field => {
79
- const fieldValue = entity[field.name];
80
- return fieldValue !== undefined && fieldValue !== null;
81
- });
38
+ private activeUserId(): number | null {
39
+ return this.requestContextService.getActiveUser()?.sub ?? null;
82
40
  }
83
41
 
84
42
  async afterInsert(event: InsertEvent<any>) {
85
- if (await this.shouldTrackAudit(event.entity, event.metadata)) {
86
- // await this.chatterMessageService.postAuditMessageOnInsert(event.entity, event.metadata);
87
- this.enqueue(event, {
88
- kind: 'insert',
89
- args: [event.entity, event.metadata] as Parameters<ChatterMessageService['postAuditMessageOnInsert']>,
90
- });
91
- }
43
+ if (!this.shouldTrackAudit(event.metadata)) return;
44
+ this.enqueue(event, {
45
+ eventType: 'insert',
46
+ modelName: event.metadata.name,
47
+ entityId: event.entity?.id ?? null,
48
+ occurredAt: new Date().toISOString(),
49
+ after: event.entity ?? null,
50
+ userId: this.activeUserId(),
51
+ });
92
52
  }
93
53
 
94
54
  async afterUpdate(event: UpdateEvent<any>) {
95
- if (await this.shouldTrackAudit(event.entity, event.metadata)) {
96
- // await this.chatterMessageService.postAuditMessageOnUpdate(event.entity, event.metadata, event.databaseEntity, event.updatedColumns || []);
97
- this.enqueue(event, {
98
- kind: 'update',
99
- args: [
100
- event.entity, // entity (after)
101
- event.metadata,
102
- event.databaseEntity, // entity (before)
103
- event.updatedColumns ?? [],
104
- ] as Parameters<ChatterMessageService['postAuditMessageOnUpdate']>,
105
- });
106
- }
55
+ if (!this.shouldTrackAudit(event.metadata)) return;
56
+ this.enqueue(event, {
57
+ eventType: 'update',
58
+ modelName: event.metadata.name,
59
+ entityId: event.entity?.id ?? null,
60
+ occurredAt: new Date().toISOString(),
61
+ after: event.entity ?? null,
62
+ // databaseEntity is only populated when the entity was fetched first (save() path).
63
+ // QueryBuilder update() leaves this undefined; postAuditMessageOnUpdate guards for it.
64
+ before: event.databaseEntity ?? null,
65
+ updatedColumnNames: (event.updatedColumns ?? []).map(c => c.propertyName),
66
+ userId: this.activeUserId(),
67
+ });
107
68
  }
108
69
 
109
70
  async afterRemove(event: RemoveEvent<any>) {
110
- if (await this.shouldTrackAudit(event.entity, event.metadata)) {
111
- // await this.chatterMessageService.postAuditMessageOnDelete(event.entity, event.metadata, event.databaseEntity);
112
- this.enqueue(event, {
113
- kind: 'delete',
114
- args: [
115
- event.entity,
116
- event.metadata,
117
- event.databaseEntity,
118
- ] as Parameters<ChatterMessageService['postAuditMessageOnDelete']>,
119
- });
120
- }
71
+ if (!this.shouldTrackAudit(event.metadata)) return;
72
+ this.enqueue(event, {
73
+ eventType: 'delete',
74
+ modelName: event.metadata.name,
75
+ entityId: event.databaseEntity?.id ?? null,
76
+ occurredAt: new Date().toISOString(),
77
+ before: event.databaseEntity,
78
+ userId: this.activeUserId(),
79
+ });
121
80
  }
122
81
 
123
82
  // --------- transaction lifecycle ----------
@@ -125,191 +84,24 @@ export class AuditSubscriber implements EntitySubscriberInterface {
125
84
  const batch = this.perTxn.get(event.queryRunner) ?? [];
126
85
  this.perTxn.delete(event.queryRunner);
127
86
 
128
- // Now we’re OUTSIDE the DB transaction — safe to do I/O/DB writes inside chatter service.
129
- for (const item of batch) {
130
- try {
131
- switch (item.kind) {
132
- case 'insert': await this.chatterMessageService.postAuditMessageOnInsert(...item.args); break;
133
- case 'update': await this.chatterMessageService.postAuditMessageOnUpdate(...item.args); break;
134
- case 'delete': await this.chatterMessageService.postAuditMessageOnDelete(...item.args); break;
135
- }
136
- } catch (e) {
137
- // Best effort: log and continue; your core txn was already committed
138
- // Optionally: send to a generic error logger/metric here
87
+ // Now outside the DB transaction — safe to publish to the queue.
88
+ // allSettled: publish in parallel; a single failure does not block the rest.
89
+ const results = await Promise.allSettled(
90
+ batch.map(payload => this.publisherFactory.publish({ payload }, 'ChatterQueuePublisher'))
91
+ );
92
+
93
+ results.forEach((result, i) => {
94
+ if (result.status === 'rejected') {
95
+ this.logger.error(
96
+ `Failed to publish audit event for ${batch[i].modelName}#${batch[i].entityId}`,
97
+ result.reason,
98
+ );
139
99
  }
140
- }
100
+ });
141
101
  }
142
102
 
143
103
  afterTransactionRollback(event: { queryRunner: any }) {
144
- // Drop buffered calls; the write never happened
104
+ // Drop buffered payloads; the write never happened.
145
105
  this.perTxn.delete(event.queryRunner);
146
106
  }
147
107
  }
148
-
149
- // import { DataSource, EntityMetadata, EntitySubscriberInterface, EventSubscriber, InsertEvent, RemoveEvent, UpdateEvent } from 'typeorm';
150
- // import { Injectable } from '@nestjs/common';
151
- // import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
152
- // import { Repository } from 'typeorm';
153
- // import { ModelMetadata } from '../entities/model-metadata.entity';
154
- // import { lowerFirst } from 'src/helpers/string.helper';
155
- // import { ModelMetadataHelperService } from 'src/helpers/model-metadata-helper.service';
156
- // import { ChatterMessagePayload } from 'src/jobs/chatter-queue-publisher.service';
157
- // import { RequestContextService } from 'src/services/request-context.service';
158
- // import { PublisherFactory } from 'src/services/queues/publisher-factory.service';
159
-
160
- // @EventSubscriber()
161
- // @Injectable()
162
- // export class AuditSubscriber implements EntitySubscriberInterface {
163
- // private perTxn = new WeakMap<any, ChatterMessagePayload[]>();
164
-
165
- // constructor(
166
- // @InjectDataSource() private readonly dataSource: DataSource,
167
- // @InjectRepository(ModelMetadata) private readonly modelMetadataRepo: Repository<ModelMetadata>,
168
- // private readonly modelMetadataHelperService: ModelMetadataHelperService,
169
- // private readonly requestContext: RequestContextService,
170
- // private readonly publisherFactory: PublisherFactory<any>
171
- // ) {
172
- // this.dataSource.subscribers.push(this);
173
- // }
174
-
175
- // // --- small cache to avoid metadata queries on every row ---
176
- // private modelCache = new Map<string, { enable: boolean; fields: Array<{ name: string; enableAuditTracking: boolean; type: string; relationType?: string }>; ts: number }>();
177
- // private cacheTTLms = 60_000;
178
-
179
- // private async shouldTrackAudit(entity: any, metadata: EntityMetadata): Promise<{ enable: boolean; auditFields?: string[] }> {
180
- // const key = metadata.name;
181
- // const now = Date.now();
182
- // const cached = this.modelCache.get(key);
183
- // if (cached && (now - cached.ts) < this.cacheTTLms) {
184
- // if (!cached.enable) return { enable: false };
185
- // const fields = cached.fields.filter(f =>
186
- // f.enableAuditTracking &&
187
- // !['mediaSingle', 'mediaMultiple', 'computed', 'richText', 'json'].includes(f.type) &&
188
- // !(f.type === 'relation' && f.relationType === 'one-to-many')
189
- // );
190
- // const present = fields.map(f => f.name).filter(n => entity?.[n] !== undefined);
191
- // return { enable: present.length > 0, auditFields: present };
192
- // }
193
-
194
- // const model = await this.modelMetadataRepo.findOne({
195
- // where: { singularName: lowerFirst(metadata.name) },
196
- // relations: { fields: true, module: true },
197
- // });
198
- // const enable = !!model?.enableAuditTracking;
199
- // const fields = model?.fields ?? [];
200
- // this.modelCache.set(key, { enable, fields, ts: now });
201
-
202
- // if (!enable) return { enable: false };
203
- // const filtered = fields.filter(f =>
204
- // f.enableAuditTracking &&
205
- // !['mediaSingle', 'mediaMultiple', 'computed', 'richText', 'json'].includes(f.type) &&
206
- // !(f.type === 'relation' && f.relationType === 'one-to-many')
207
- // );
208
- // const present = filtered.map(f => f.name).filter(n => entity?.[n] !== undefined);
209
- // return { enable: present.length > 0, auditFields: present };
210
- // }
211
-
212
- // private push(event: { queryRunner: any }, msg: ChatterMessagePayload) {
213
- // const arr = this.perTxn.get(event.queryRunner) ?? [];
214
- // arr.push(msg);
215
- // this.perTxn.set(event.queryRunner, arr);
216
- // }
217
-
218
- // async afterInsert(event: InsertEvent<any>) {
219
- // if (!event.entity) return;
220
- // const enable = await this.shouldTrackAudit(event.entity, event.metadata);
221
- // if (!enable) return;
222
-
223
- // const payload: ChatterMessagePayload = {
224
- // eventType: 'insert',
225
- // model: event.metadata.name,
226
- // entityId: String(event.entity.id ?? event.entity.uuid ?? ''),
227
- // occurredAt: new Date().toISOString(),
228
- // after: this.safeCopy(event.entity),
229
- // userId: this.getUserId(),
230
- // };
231
- // this.push(event, payload);
232
- // }
233
-
234
- // async afterUpdate(event: UpdateEvent<any>) {
235
- // // Updated entity may be null if you used raw query; fall back to databaseEntity
236
- // const current = event.entity ?? {};
237
- // const before = event.databaseEntity ?? {};
238
- // const { enable, auditFields } = await this.shouldTrackAudit(current, event.metadata);
239
- // if (!enable) return;
240
-
241
- // const changedCols = (event.updatedColumns || []).map(c => c.propertyName);
242
- // const payload: ChatterMessagePayload = {
243
- // eventType: 'update',
244
- // model: event.metadata.name,
245
- // entityId: String((current as any).id ?? (before as any).id ?? ''),
246
- // occurredAt: new Date().toISOString(),
247
- // before: this.pick(before, auditFields || changedCols),
248
- // after: this.pick(current, auditFields || changedCols),
249
- // diff: changedCols,
250
- // userId: this.getUserId(),
251
- // };
252
- // this.push(event, payload);
253
- // }
254
-
255
- // async afterRemove(event: RemoveEvent<any>) {
256
- // const base = event.entity ?? event.databaseEntity;
257
- // if (!base) return;
258
-
259
- // const { enable } = await this.shouldTrackAudit(base, event.metadata);
260
- // if (!enable) return;
261
-
262
- // const payload: ChatterMessagePayload = {
263
- // eventType: 'delete',
264
- // model: event.metadata.name,
265
- // entityId: String((base as any).id ?? ''),
266
- // occurredAt: new Date().toISOString(),
267
- // before: this.safeCopy(base),
268
- // userId: this.getUserId(),
269
- // };
270
- // this.push(event, payload);
271
- // }
272
-
273
- // // Publish AFTER the transaction commits -> no idle-in-transaction
274
- // async afterTransactionCommit(event: { queryRunner: any }) {
275
- // const batch = this.perTxn.get(event.queryRunner) ?? [];
276
- // this.perTxn.delete(event.queryRunner);
277
- // for (const msg of batch) {
278
- // try {
279
- // await this.publisherFactory.publish({ payload: msg, parentEntity: msg.model, parentEntityId: msg.entityId }, 'ChatterQueuePublisher');
280
- // } catch (err) {
281
- // // log + optionally send to a DLQ or retry queue
282
- // // do NOT throw; commit already happened
283
- // // your RabbitMqPublisher likely tracks failures in MqMessage tables anyway
284
- // }
285
- // }
286
- // }
287
-
288
- // afterTransactionRollback(event: { queryRunner: any }) {
289
- // this.perTxn.delete(event.queryRunner);
290
- // }
291
-
292
- // // --- small helpers to keep payloads JSON-safe and small ---
293
- // private safeCopy(obj: any) {
294
- // try {
295
- // return JSON.parse(JSON.stringify(obj));
296
- // } catch {
297
- // return {}; // strip circular refs
298
- // }
299
- // }
300
-
301
- // private pick(obj: any, keys: string[]) {
302
- // const out: any = {};
303
- // for (const k of keys) out[k] = obj?.[k];
304
- // return this.safeCopy(out);
305
- // }
306
-
307
- // private getUserId(): string | null {
308
-
309
- // const activeUser = this.requestContext.getActiveUser();
310
- // if (activeUser?.sub)
311
- // return String(activeUser.sub);
312
- // }
313
-
314
-
315
- // }
@@ -78,9 +78,9 @@ export class ComputedEntityFieldSubscriber implements EntitySubscriberInterface
78
78
  modelName
79
79
  );
80
80
  //TODO: We can add a feature i.e dependsOn, where we can check if the computed field depends on other computed fields and evaluate them first
81
- await Promise.all(
82
- computedFieldsTobeEvaluated.map(c => this.evaluateComputedField(this.attachContext(c, eventContext), entity, currentOperation))
83
- )
81
+ for (const computedField of computedFieldsTobeEvaluated) {
82
+ await this.evaluateComputedField(this.attachContext(computedField, eventContext), entity, currentOperation);
83
+ }
84
84
  }
85
85
 
86
86
  private handleComputedFieldEvaluationJob(entity: any, currentOperation: ComputedFieldTriggerOperation, modelName: string, eventContext?: TypeOrmEventContext) {
@@ -1,11 +1,9 @@
1
1
  import { Injectable, Scope } from "@nestjs/common";
2
2
  import { InjectDataSource } from "@nestjs/typeorm";
3
- import { User } from "src/entities/user.entity";
4
- import { ActiveUserData } from "src/interfaces/active-user-data.interface";
5
3
  import { RequestContextService } from "src/services/request-context.service";
6
- import { DataSource, EntitySubscriberInterface, EventSubscriber, InsertEvent, UpdateEvent } from "typeorm";
4
+ import { DataSource, EntitySubscriberInterface, InsertEvent, UpdateEvent } from "typeorm";
7
5
 
8
- @Injectable({scope: Scope.TRANSIENT})
6
+ @Injectable({ scope: Scope.TRANSIENT })
9
7
  // @EventSubscriber()
10
8
  export class CreatedByUpdatedBySubscriber implements EntitySubscriberInterface {
11
9
  private dataSource: DataSource;
@@ -30,7 +28,7 @@ export class CreatedByUpdatedBySubscriber implements EntitySubscriberInterface {
30
28
  await this.stampUserField(event, false);
31
29
  }
32
30
 
33
- private async stampUserField(event: InsertEvent<any> | UpdateEvent<any>, isInsert: boolean){
31
+ private async stampUserField(event: InsertEvent<any> | UpdateEvent<any>, isInsert: boolean) {
34
32
  if (!event.entity) {
35
33
  return;
36
34
  }
@@ -40,21 +38,29 @@ export class CreatedByUpdatedBySubscriber implements EntitySubscriberInterface {
40
38
  return;
41
39
  }
42
40
 
43
- const loadedUser = await this.loadUser(activeUserOrUndefined as unknown as ActiveUserData);
41
+ // const loadedUser = await this.loadUser(activeUserOrUndefined as unknown as ActiveUserData);
42
+ // if (isInsert) {
43
+ // event.entity.createdBy = loadedUser?.id;
44
+ // event.entity.updatedBy = loadedUser?.id; // For insert, we set both createdBy and updatedBy to the same user
45
+ // }
46
+ // else {
47
+ // event.entity.updatedBy = loadedUser?.id;
48
+ // }
49
+
44
50
  if (isInsert) {
45
- event.entity.createdBy = loadedUser?.id;
46
- event.entity.updatedBy = loadedUser?.id; // For insert, we set both createdBy and updatedBy to the same user
51
+ event.entity.createdBy = activeUserOrUndefined?.sub;
52
+ event.entity.updatedBy = activeUserOrUndefined?.sub; // For insert, we set both createdBy and updatedBy to the same user
47
53
  }
48
54
  else {
49
- event.entity.updatedBy = loadedUser?.id;
55
+ event.entity.updatedBy = activeUserOrUndefined?.sub;
50
56
  }
51
57
  }
52
58
 
53
- private async loadUser(activeUser: ActiveUserData): Promise<User> {
54
- const userRepo = this.defaultDataSource.getRepository(User); // Assuming 'User' is the entity name for users in your application
55
- const loadedUser = await userRepo.findOne({
56
- where: { id: activeUser.sub }, // Assuming 'sub' is the user ID in the JWT token
57
- });
58
- return loadedUser;;
59
- }
59
+ // private async loadUser(activeUser: ActiveUserData): Promise<User> {
60
+ // const userRepo = this.defaultDataSource.getRepository(User); // Assuming 'User' is the entity name for users in your application
61
+ // const loadedUser = await userRepo.findOne({
62
+ // where: { id: activeUser.sub }, // Assuming 'sub' is the user ID in the JWT token
63
+ // });
64
+ // return loadedUser;;
65
+ // }
60
66
  }
@@ -4,9 +4,10 @@ import { Logger } from 'winston';
4
4
  import { Inject } from '@nestjs/common';
5
5
  import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
6
6
  import * as winston from 'winston';
7
+ import { Environment } from './decorators/disallow-in-production.decorator';
7
8
 
8
9
  export const WinstonLoggerConfig = {
9
- level: 'debug', // Allow all log levels for debugging
10
+ level: process.env.LOG_LEVEL || (process.env.ENV === Environment.Production ? 'warn' : 'debug'),
10
11
  format: winston.format.combine(
11
12
  winston.format.timestamp(),
12
13
  winston.format.errors({ stack: true }),
@@ -1,119 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const test_1 = require("@playwright/test");
4
- const env_1 = require("../helpers/env");
5
- const baseURL = process.env.API_BASE_URL ?? "http://localhost:3000";
6
- const TEST_USER_EMAIL = (0, env_1.getRequiredEnv)("TEST_USER_EMAIL");
7
- const TEST_USER_PASSWORD = (0, env_1.getRequiredEnv)("TEST_USER_PASSWORD");
8
- function base64UrlDecode(input) {
9
- const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
10
- const padded = normalized.length % 4 === 0
11
- ? normalized
12
- : normalized.padEnd(normalized.length + (4 - (normalized.length % 4)), "=");
13
- return Buffer.from(padded, "base64").toString("utf-8");
14
- }
15
- function validateJwt(token) {
16
- const parts = token.split(".");
17
- if (parts.length !== 3) {
18
- throw new Error("JWT must have three dot-separated parts.");
19
- }
20
- const headerJson = JSON.parse(base64UrlDecode(parts[0]));
21
- const payloadJson = JSON.parse(base64UrlDecode(parts[1]));
22
- if (!headerJson || typeof headerJson !== "object") {
23
- throw new Error("JWT header must be a JSON object.");
24
- }
25
- if (!payloadJson || typeof payloadJson !== "object") {
26
- throw new Error("JWT payload must be a JSON object.");
27
- }
28
- if (typeof payloadJson.exp !== "number") {
29
- throw new Error("JWT payload.exp must be a number.");
30
- }
31
- return payloadJson;
32
- }
33
- (0, test_1.test)("API: authenticate succeeds with valid credentials", async () => {
34
- const api = await test_1.request.newContext({
35
- baseURL,
36
- extraHTTPHeaders: {
37
- accept: "*/*",
38
- "content-type": "application/json",
39
- },
40
- });
41
- try {
42
- const res = await api.post("/api/iam/authenticate", {
43
- data: {
44
- email: TEST_USER_EMAIL,
45
- username: "",
46
- password: TEST_USER_PASSWORD,
47
- },
48
- });
49
- (0, test_1.expect)(res.status()).toBe(200);
50
- const json = await res.json();
51
- (0, test_1.expect)(json.statusCode).toBe(200);
52
- (0, test_1.expect)(Array.isArray(json.message)).toBe(true);
53
- (0, test_1.expect)(json.message.length).toBe(0);
54
- (0, test_1.expect)(json.error).toBe("");
55
- const user = json.data?.user;
56
- (0, test_1.expect)(user, "Expected data.user to be an object.").toBeTruthy();
57
- (0, test_1.expect)(typeof user).toBe("object");
58
- const email = user?.email;
59
- (0, test_1.expect)(typeof email).toBe("string");
60
- if (email === TEST_USER_EMAIL) {
61
- (0, test_1.expect)(email).toBe(TEST_USER_EMAIL);
62
- }
63
- else {
64
- (0, test_1.expect)(email.length).toBeGreaterThan(0);
65
- }
66
- (0, test_1.expect)(typeof user?.mobile).toBe("string");
67
- (0, test_1.expect)(typeof user?.username).toBe("string");
68
- (0, test_1.expect)(typeof user?.forcePasswordChange).toBe("boolean");
69
- (0, test_1.expect)(typeof user?.id).toBe("number");
70
- const roles = user?.roles;
71
- (0, test_1.expect)(Array.isArray(roles)).toBe(true);
72
- if (Array.isArray(roles)) {
73
- (0, test_1.expect)(roles.every((role) => typeof role === "string")).toBe(true);
74
- (0, test_1.expect)(roles).toContain("Admin");
75
- }
76
- const accessToken = json.data?.accessToken;
77
- const refreshToken = json.data?.refreshToken;
78
- (0, test_1.expect)(typeof accessToken).toBe("string");
79
- (0, test_1.expect)(typeof refreshToken).toBe("string");
80
- const accessPayload = validateJwt(accessToken);
81
- const refreshPayload = validateJwt(refreshToken);
82
- (0, test_1.expect)(typeof accessPayload.exp).toBe("number");
83
- (0, test_1.expect)(typeof refreshPayload.exp).toBe("number");
84
- }
85
- finally {
86
- await api.dispose();
87
- }
88
- });
89
- (0, test_1.test)("API: authenticate fails with wrong password", async () => {
90
- const api = await test_1.request.newContext({
91
- baseURL,
92
- extraHTTPHeaders: {
93
- accept: "*/*",
94
- "content-type": "application/json",
95
- },
96
- });
97
- try {
98
- const res = await api.post("/api/iam/authenticate", {
99
- data: {
100
- email: TEST_USER_EMAIL,
101
- username: "",
102
- password: `${TEST_USER_PASSWORD}__wrong`,
103
- },
104
- });
105
- (0, test_1.expect)(res.status()).toBe(401);
106
- const json = await res.json();
107
- (0, test_1.expect)(json.statusCode).toBe(401);
108
- (0, test_1.expect)(json.statusCodeMessage).toBe("Unauthorized");
109
- (0, test_1.expect)(json.message).toBe("Invalid credentials");
110
- (0, test_1.expect)(json.error).toBe("Invalid credentials");
111
- (0, test_1.expect)(json.data?.statusCode).toBe(401);
112
- (0, test_1.expect)(json.data?.error).toBe("Unauthorized");
113
- (0, test_1.expect)(json.data?.message).toBe("Invalid credentials");
114
- }
115
- finally {
116
- await api.dispose();
117
- }
118
- });
119
- //# sourceMappingURL=authenticate.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"authenticate.spec.js","sourceRoot":"","sources":["../../tests/api/authenticate.spec.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, request, test } from \"@playwright/test\";\nimport { getRequiredEnv } from \"../helpers/env\";\n\ntype JwtPayload = {\n exp?: number;\n [key: string]: unknown;\n};\n\nconst baseURL = process.env.API_BASE_URL ?? \"http://localhost:3000\";\nconst TEST_USER_EMAIL = getRequiredEnv(\"TEST_USER_EMAIL\");\nconst TEST_USER_PASSWORD = getRequiredEnv(\"TEST_USER_PASSWORD\");\n\nfunction base64UrlDecode(input: string): string {\n const normalized = input.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded =\n normalized.length % 4 === 0\n ? normalized\n : normalized.padEnd(\n normalized.length + (4 - (normalized.length % 4)),\n \"=\"\n );\n return Buffer.from(padded, \"base64\").toString(\"utf-8\");\n}\n\nfunction validateJwt(token: string): JwtPayload {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw new Error(\"JWT must have three dot-separated parts.\");\n }\n\n const headerJson = JSON.parse(base64UrlDecode(parts[0]));\n const payloadJson = JSON.parse(base64UrlDecode(parts[1]));\n\n if (!headerJson || typeof headerJson !== \"object\") {\n throw new Error(\"JWT header must be a JSON object.\");\n }\n\n if (!payloadJson || typeof payloadJson !== \"object\") {\n throw new Error(\"JWT payload must be a JSON object.\");\n }\n\n if (typeof payloadJson.exp !== \"number\") {\n throw new Error(\"JWT payload.exp must be a number.\");\n }\n\n return payloadJson as JwtPayload;\n}\n\ntest(\"API: authenticate succeeds with valid credentials\", async () => {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: {\n accept: \"*/*\",\n \"content-type\": \"application/json\",\n },\n });\n\n try {\n const res = await api.post(\"/api/iam/authenticate\", {\n data: {\n email: TEST_USER_EMAIL,\n username: \"\",\n password: TEST_USER_PASSWORD,\n },\n });\n\n expect(res.status()).toBe(200);\n const json = await res.json();\n\n expect(json.statusCode).toBe(200);\n expect(Array.isArray(json.message)).toBe(true);\n expect(json.message.length).toBe(0);\n expect(json.error).toBe(\"\");\n\n const user = json.data?.user as Record<string, unknown> | undefined;\n expect(user, \"Expected data.user to be an object.\").toBeTruthy();\n expect(typeof user).toBe(\"object\");\n\n const email = user?.email;\n expect(typeof email).toBe(\"string\");\n if (email === TEST_USER_EMAIL) {\n expect(email).toBe(TEST_USER_EMAIL);\n } else {\n // If your API returns a fixed system email, replace this with an exact match.\n expect((email as string).length).toBeGreaterThan(0);\n }\n\n expect(typeof user?.mobile).toBe(\"string\");\n expect(typeof user?.username).toBe(\"string\");\n expect(typeof user?.forcePasswordChange).toBe(\"boolean\");\n expect(typeof user?.id).toBe(\"number\");\n\n const roles = user?.roles;\n expect(Array.isArray(roles)).toBe(true);\n if (Array.isArray(roles)) {\n expect(roles.every((role) => typeof role === \"string\")).toBe(true);\n expect(roles).toContain(\"Admin\");\n }\n\n const accessToken = json.data?.accessToken;\n const refreshToken = json.data?.refreshToken;\n expect(typeof accessToken).toBe(\"string\");\n expect(typeof refreshToken).toBe(\"string\");\n\n const accessPayload = validateJwt(accessToken as string);\n const refreshPayload = validateJwt(refreshToken as string);\n\n expect(typeof accessPayload.exp).toBe(\"number\");\n expect(typeof refreshPayload.exp).toBe(\"number\");\n } finally {\n await api.dispose();\n }\n});\n\ntest(\"API: authenticate fails with wrong password\", async () => {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: {\n accept: \"*/*\",\n \"content-type\": \"application/json\",\n },\n });\n\n try {\n const res = await api.post(\"/api/iam/authenticate\", {\n data: {\n email: TEST_USER_EMAIL,\n username: \"\",\n password: `${TEST_USER_PASSWORD}__wrong`,\n },\n });\n\n expect(res.status()).toBe(401);\n const json = await res.json();\n\n expect(json.statusCode).toBe(401);\n expect(json.statusCodeMessage).toBe(\"Unauthorized\");\n expect(json.message).toBe(\"Invalid credentials\");\n expect(json.error).toBe(\"Invalid credentials\");\n expect(json.data?.statusCode).toBe(401);\n expect(json.data?.error).toBe(\"Unauthorized\");\n expect(json.data?.message).toBe(\"Invalid credentials\");\n } finally {\n await api.dispose();\n }\n});\n"]}