nodejs-quickstart-structure 1.18.1 → 1.19.1

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 (235) hide show
  1. package/CHANGELOG.md +309 -294
  2. package/LICENSE +15 -15
  3. package/README.md +2 -1
  4. package/lib/generator.js +139 -139
  5. package/lib/modules/app-setup.js +401 -401
  6. package/lib/modules/caching-setup.js +76 -73
  7. package/lib/modules/config-files.js +151 -151
  8. package/lib/modules/database-setup.js +116 -116
  9. package/lib/modules/kafka-setup.js +249 -191
  10. package/lib/modules/project-setup.js +32 -31
  11. package/lib/prompts.js +100 -100
  12. package/package.json +78 -67
  13. package/templates/clean-architecture/js/src/domain/models/User.js +9 -9
  14. package/templates/clean-architecture/js/src/errors/ApiError.js +14 -14
  15. package/templates/clean-architecture/js/src/errors/BadRequestError.js +11 -10
  16. package/templates/clean-architecture/js/src/errors/BadRequestError.spec.js.ejs +22 -21
  17. package/templates/clean-architecture/js/src/errors/NotFoundError.js +11 -10
  18. package/templates/clean-architecture/js/src/errors/NotFoundError.spec.js.ejs +22 -21
  19. package/templates/clean-architecture/js/src/index.js.ejs +55 -55
  20. package/templates/clean-architecture/js/src/infrastructure/config/env.js.ejs +47 -47
  21. package/templates/clean-architecture/js/src/infrastructure/log/logger.js +36 -36
  22. package/templates/clean-architecture/js/src/infrastructure/log/logger.spec.js.ejs +63 -63
  23. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +69 -39
  24. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +142 -81
  25. package/templates/clean-architecture/js/src/infrastructure/webserver/middleware/errorMiddleware.js +30 -30
  26. package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +89 -89
  27. package/templates/clean-architecture/js/src/infrastructure/webserver/swagger.js.ejs +6 -6
  28. package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +156 -75
  29. package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +234 -138
  30. package/templates/clean-architecture/js/src/interfaces/graphql/context.js.ejs +13 -13
  31. package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +31 -31
  32. package/templates/clean-architecture/js/src/interfaces/graphql/index.js.ejs +5 -5
  33. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/index.js.ejs +6 -6
  34. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.js.ejs +27 -21
  35. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +66 -49
  36. package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/index.js.ejs +6 -6
  37. package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/user.types.js.ejs +19 -17
  38. package/templates/clean-architecture/js/src/interfaces/routes/api.js +12 -10
  39. package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +38 -38
  40. package/templates/clean-architecture/js/src/usecases/CreateUser.js +14 -14
  41. package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +51 -51
  42. package/templates/clean-architecture/js/src/usecases/DeleteUser.js +11 -0
  43. package/templates/clean-architecture/js/src/usecases/DeleteUser.spec.js.ejs +47 -0
  44. package/templates/clean-architecture/js/src/usecases/GetAllUsers.js +12 -12
  45. package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +61 -61
  46. package/templates/clean-architecture/js/src/usecases/UpdateUser.js +11 -0
  47. package/templates/clean-architecture/js/src/usecases/UpdateUser.spec.js.ejs +48 -0
  48. package/templates/clean-architecture/js/src/utils/errorMessages.js +14 -0
  49. package/templates/clean-architecture/js/src/utils/httpCodes.js +9 -9
  50. package/templates/clean-architecture/ts/src/config/env.ts.ejs +46 -46
  51. package/templates/clean-architecture/ts/src/config/swagger.ts.ejs +6 -6
  52. package/templates/clean-architecture/ts/src/domain/user.ts +7 -7
  53. package/templates/clean-architecture/ts/src/errors/ApiError.ts +15 -15
  54. package/templates/clean-architecture/ts/src/errors/BadRequestError.spec.ts.ejs +22 -21
  55. package/templates/clean-architecture/ts/src/errors/BadRequestError.ts +9 -8
  56. package/templates/clean-architecture/ts/src/errors/NotFoundError.spec.ts.ejs +22 -21
  57. package/templates/clean-architecture/ts/src/errors/NotFoundError.ts +9 -8
  58. package/templates/clean-architecture/ts/src/index.ts.ejs +139 -139
  59. package/templates/clean-architecture/ts/src/infrastructure/log/logger.spec.ts.ejs +63 -63
  60. package/templates/clean-architecture/ts/src/infrastructure/log/logger.ts +36 -36
  61. package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +175 -85
  62. package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +74 -0
  63. package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +331 -185
  64. package/templates/clean-architecture/ts/src/interfaces/controllers/userController.ts.ejs +173 -84
  65. package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +32 -32
  66. package/templates/clean-architecture/ts/src/interfaces/graphql/context.ts.ejs +17 -17
  67. package/templates/clean-architecture/ts/src/interfaces/graphql/index.ts.ejs +3 -3
  68. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/index.ts.ejs +4 -4
  69. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -51
  70. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs +29 -21
  71. package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/index.ts.ejs +4 -4
  72. package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/user.types.ts.ejs +17 -15
  73. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +40 -40
  74. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts +13 -11
  75. package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +51 -51
  76. package/templates/clean-architecture/ts/src/usecases/createUser.ts +13 -13
  77. package/templates/clean-architecture/ts/src/usecases/deleteUser.spec.ts.ejs +47 -0
  78. package/templates/clean-architecture/ts/src/usecases/deleteUser.ts +9 -0
  79. package/templates/clean-architecture/ts/src/usecases/getAllUsers.spec.ts.ejs +63 -63
  80. package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts +10 -10
  81. package/templates/clean-architecture/ts/src/usecases/updateUser.spec.ts.ejs +48 -0
  82. package/templates/clean-architecture/ts/src/usecases/updateUser.ts +9 -0
  83. package/templates/clean-architecture/ts/src/utils/errorMessages.ts +12 -0
  84. package/templates/clean-architecture/ts/src/utils/errorMiddleware.ts.ejs +27 -27
  85. package/templates/clean-architecture/ts/src/utils/httpCodes.ts +7 -7
  86. package/templates/common/.cursorrules.ejs +60 -60
  87. package/templates/common/.dockerignore +12 -12
  88. package/templates/common/.env.example.ejs +41 -41
  89. package/templates/common/.gitattributes +46 -0
  90. package/templates/common/.gitlab-ci.yml.ejs +86 -86
  91. package/templates/common/.lintstagedrc +6 -6
  92. package/templates/common/.prettierrc +7 -7
  93. package/templates/common/Dockerfile +73 -73
  94. package/templates/common/Jenkinsfile.ejs +87 -87
  95. package/templates/common/README.md.ejs +294 -270
  96. package/templates/common/SECURITY.md +20 -20
  97. package/templates/common/_github/workflows/ci.yml.ejs +46 -46
  98. package/templates/common/_github/workflows/security.yml.ejs +36 -36
  99. package/templates/common/_gitignore +5 -5
  100. package/templates/common/_husky/pre-commit +4 -4
  101. package/templates/common/caching/clean/js/CreateUser.js.ejs +29 -29
  102. package/templates/common/caching/clean/js/DeleteUser.js.ejs +27 -0
  103. package/templates/common/caching/clean/js/GetAllUsers.js.ejs +37 -37
  104. package/templates/common/caching/clean/js/UpdateUser.js.ejs +27 -0
  105. package/templates/common/caching/clean/ts/createUser.ts.ejs +27 -27
  106. package/templates/common/caching/clean/ts/deleteUser.ts.ejs +24 -0
  107. package/templates/common/caching/clean/ts/getAllUsers.ts.ejs +34 -34
  108. package/templates/common/caching/clean/ts/updateUser.ts.ejs +25 -0
  109. package/templates/common/caching/js/memoryCache.js.ejs +60 -60
  110. package/templates/common/caching/js/memoryCache.spec.js.ejs +101 -101
  111. package/templates/common/caching/js/redisClient.js.ejs +75 -75
  112. package/templates/common/caching/js/redisClient.spec.js.ejs +147 -147
  113. package/templates/common/caching/ts/memoryCache.spec.ts.ejs +102 -102
  114. package/templates/common/caching/ts/memoryCache.ts.ejs +73 -64
  115. package/templates/common/caching/ts/redisClient.spec.ts.ejs +157 -157
  116. package/templates/common/caching/ts/redisClient.ts.ejs +89 -80
  117. package/templates/common/database/js/database.js.ejs +19 -19
  118. package/templates/common/database/js/database.spec.js.ejs +56 -56
  119. package/templates/common/database/js/models/User.js.ejs +79 -53
  120. package/templates/common/database/js/models/User.js.mongoose.ejs +23 -19
  121. package/templates/common/database/js/models/User.spec.js.ejs +94 -84
  122. package/templates/common/database/js/mongoose.js.ejs +33 -33
  123. package/templates/common/database/js/mongoose.spec.js.ejs +43 -43
  124. package/templates/common/database/ts/database.spec.ts.ejs +56 -56
  125. package/templates/common/database/ts/database.ts.ejs +21 -21
  126. package/templates/common/database/ts/models/User.spec.ts.ejs +100 -84
  127. package/templates/common/database/ts/models/User.ts.ejs +87 -61
  128. package/templates/common/database/ts/models/User.ts.mongoose.ejs +30 -25
  129. package/templates/common/database/ts/mongoose.spec.ts.ejs +42 -42
  130. package/templates/common/database/ts/mongoose.ts.ejs +28 -28
  131. package/templates/common/docker-compose.yml.ejs +159 -159
  132. package/templates/common/ecosystem.config.js.ejs +40 -40
  133. package/templates/common/eslint.config.mjs.ejs +77 -77
  134. package/templates/common/health/js/healthRoute.js.ejs +50 -47
  135. package/templates/common/health/js/healthRoute.spec.js.ejs +70 -70
  136. package/templates/common/health/ts/healthRoute.spec.ts.ejs +76 -76
  137. package/templates/common/health/ts/healthRoute.ts.ejs +49 -46
  138. package/templates/common/jest.config.js.ejs +32 -32
  139. package/templates/common/jest.e2e.config.js.ejs +8 -8
  140. package/templates/common/kafka/js/config/kafka.js +9 -9
  141. package/templates/common/kafka/js/config/kafka.spec.js.ejs +27 -27
  142. package/templates/common/kafka/js/messaging/baseConsumer.js.ejs +30 -30
  143. package/templates/common/kafka/js/messaging/baseConsumer.spec.js.ejs +58 -58
  144. package/templates/common/kafka/js/messaging/userEventSchema.js.ejs +12 -11
  145. package/templates/common/kafka/js/messaging/userEventSchema.spec.js.ejs +27 -27
  146. package/templates/common/kafka/js/messaging/welcomeEmailConsumer.js.ejs +44 -31
  147. package/templates/common/kafka/js/messaging/welcomeEmailConsumer.spec.js.ejs +86 -49
  148. package/templates/common/kafka/js/services/kafkaService.js.ejs +93 -93
  149. package/templates/common/kafka/js/services/kafkaService.spec.js.ejs +106 -106
  150. package/templates/common/kafka/js/utils/kafkaEvents.js.ejs +7 -0
  151. package/templates/common/kafka/ts/config/kafka.spec.ts.ejs +27 -27
  152. package/templates/common/kafka/ts/config/kafka.ts +7 -7
  153. package/templates/common/kafka/ts/messaging/baseConsumer.spec.ts.ejs +50 -50
  154. package/templates/common/kafka/ts/messaging/baseConsumer.ts.ejs +27 -27
  155. package/templates/common/kafka/ts/messaging/userEventSchema.spec.ts.ejs +51 -51
  156. package/templates/common/kafka/ts/messaging/userEventSchema.ts.ejs +12 -11
  157. package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.spec.ts.ejs +86 -49
  158. package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.ts.ejs +38 -25
  159. package/templates/common/kafka/ts/services/kafkaService.spec.ts.ejs +81 -81
  160. package/templates/common/kafka/ts/services/kafkaService.ts.ejs +95 -95
  161. package/templates/common/kafka/ts/utils/kafkaEvents.ts.ejs +5 -0
  162. package/templates/common/migrate-mongo-config.js.ejs +31 -31
  163. package/templates/common/migrations/init.js.ejs +23 -23
  164. package/templates/common/package.json.ejs +119 -118
  165. package/templates/common/prompts/add-feature.md.ejs +26 -26
  166. package/templates/common/prompts/project-context.md.ejs +43 -43
  167. package/templates/common/prompts/troubleshoot.md.ejs +28 -28
  168. package/templates/common/public/css/style.css +147 -147
  169. package/templates/common/scripts/run-e2e.js.ejs +63 -63
  170. package/templates/common/shutdown/js/gracefulShutdown.js.ejs +65 -61
  171. package/templates/common/shutdown/js/gracefulShutdown.spec.js.ejs +149 -160
  172. package/templates/common/shutdown/ts/gracefulShutdown.spec.ts.ejs +179 -158
  173. package/templates/common/shutdown/ts/gracefulShutdown.ts.ejs +59 -55
  174. package/templates/common/sonar-project.properties.ejs +27 -27
  175. package/templates/common/src/tests/e2e/e2e.users.test.js.ejs +120 -49
  176. package/templates/common/src/tests/e2e/e2e.users.test.ts.ejs +120 -49
  177. package/templates/common/src/utils/errorMiddleware.spec.js.ejs +79 -79
  178. package/templates/common/src/utils/errorMiddleware.spec.ts.ejs +94 -94
  179. package/templates/common/swagger.yml.ejs +118 -66
  180. package/templates/common/tsconfig.json +22 -22
  181. package/templates/common/views/ejs/index.ejs +55 -55
  182. package/templates/common/views/pug/index.pug +40 -40
  183. package/templates/db/mysql/V1__Initial_Setup.sql.ejs +10 -9
  184. package/templates/db/postgres/V1__Initial_Setup.sql.ejs +10 -9
  185. package/templates/mvc/js/src/config/env.js.ejs +46 -46
  186. package/templates/mvc/js/src/config/swagger.js.ejs +6 -6
  187. package/templates/mvc/js/src/controllers/userController.js.ejs +246 -105
  188. package/templates/mvc/js/src/controllers/userController.spec.js.ejs +481 -209
  189. package/templates/mvc/js/src/errors/ApiError.js +14 -14
  190. package/templates/mvc/js/src/errors/BadRequestError.js +11 -10
  191. package/templates/mvc/js/src/errors/BadRequestError.spec.js.ejs +22 -21
  192. package/templates/mvc/js/src/errors/NotFoundError.js +11 -10
  193. package/templates/mvc/js/src/errors/NotFoundError.spec.js.ejs +22 -21
  194. package/templates/mvc/js/src/graphql/context.js.ejs +7 -7
  195. package/templates/mvc/js/src/graphql/context.spec.js.ejs +29 -29
  196. package/templates/mvc/js/src/graphql/index.js.ejs +5 -5
  197. package/templates/mvc/js/src/graphql/resolvers/index.js.ejs +6 -6
  198. package/templates/mvc/js/src/graphql/resolvers/user.resolvers.js.ejs +25 -19
  199. package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +64 -47
  200. package/templates/mvc/js/src/graphql/typeDefs/index.js.ejs +6 -6
  201. package/templates/mvc/js/src/graphql/typeDefs/user.types.js.ejs +19 -17
  202. package/templates/mvc/js/src/index.js.ejs +136 -136
  203. package/templates/mvc/js/src/routes/api.js +10 -8
  204. package/templates/mvc/js/src/routes/api.spec.js.ejs +41 -36
  205. package/templates/mvc/js/src/utils/errorMessages.js +14 -0
  206. package/templates/mvc/js/src/utils/errorMiddleware.js +29 -29
  207. package/templates/mvc/js/src/utils/httpCodes.js +9 -9
  208. package/templates/mvc/js/src/utils/logger.js +40 -40
  209. package/templates/mvc/js/src/utils/logger.spec.js.ejs +63 -63
  210. package/templates/mvc/ts/src/config/env.ts.ejs +45 -45
  211. package/templates/mvc/ts/src/config/swagger.ts.ejs +6 -6
  212. package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +481 -203
  213. package/templates/mvc/ts/src/controllers/userController.ts.ejs +248 -107
  214. package/templates/mvc/ts/src/errors/ApiError.ts +15 -15
  215. package/templates/mvc/ts/src/errors/BadRequestError.spec.ts.ejs +22 -21
  216. package/templates/mvc/ts/src/errors/BadRequestError.ts +9 -8
  217. package/templates/mvc/ts/src/errors/NotFoundError.spec.ts.ejs +27 -21
  218. package/templates/mvc/ts/src/errors/NotFoundError.ts +9 -8
  219. package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +30 -30
  220. package/templates/mvc/ts/src/graphql/context.ts.ejs +12 -12
  221. package/templates/mvc/ts/src/graphql/index.ts.ejs +3 -3
  222. package/templates/mvc/ts/src/graphql/resolvers/index.ts.ejs +4 -4
  223. package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -51
  224. package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.ts.ejs +29 -21
  225. package/templates/mvc/ts/src/graphql/typeDefs/index.ts.ejs +4 -4
  226. package/templates/mvc/ts/src/graphql/typeDefs/user.types.ts.ejs +17 -15
  227. package/templates/mvc/ts/src/index.ts.ejs +156 -153
  228. package/templates/mvc/ts/src/routes/api.spec.ts.ejs +59 -40
  229. package/templates/mvc/ts/src/routes/api.ts +12 -10
  230. package/templates/mvc/ts/src/utils/errorMessages.ts +12 -0
  231. package/templates/mvc/ts/src/utils/errorMiddleware.ts.ejs +27 -27
  232. package/templates/mvc/ts/src/utils/httpCodes.ts +7 -7
  233. package/templates/mvc/ts/src/utils/logger.spec.ts.ejs +63 -63
  234. package/templates/mvc/ts/src/utils/logger.ts +36 -36
  235. package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.ts.ejs +0 -37
@@ -1,58 +1,58 @@
1
- const BaseConsumer = require('<% if (architecture === "Clean Architecture") { %>@/interfaces/messaging/baseConsumer<% } else { %>@/messaging/baseConsumer<% } %>');
2
- const logger = require('<% if (architecture === "Clean Architecture") { %>@/infrastructure/log/logger<% } else { %>@/utils/logger<% } %>');
3
-
4
- jest.mock('<% if (architecture === "Clean Architecture") { %>@/infrastructure/log/logger<% } else { %>@/utils/logger<% } %>');
5
-
6
- class TestConsumer extends BaseConsumer {
7
- constructor() {
8
- super();
9
- this._topic = 'test-topic';
10
- }
11
- get topic() { return this._topic; }
12
- get groupId() { return 'test-group'; }
13
- async handle(data) {
14
- logger.info('Handled', data);
15
- }
16
- }
17
-
18
- describe('BaseConsumer', () => {
19
- let consumer;
20
-
21
- beforeEach(() => {
22
- jest.clearAllMocks();
23
- consumer = new TestConsumer();
24
- });
25
-
26
- it('should throw error if instantiated directly', () => {
27
- expect(() => new BaseConsumer()).toThrow("Abstract class 'BaseConsumer' cannot be instantiated.");
28
- });
29
-
30
- it('should process a valid message', async () => {
31
- const message = { value: Buffer.from(JSON.stringify({ test: 'data' })) };
32
- await consumer.onMessage({ message });
33
- expect(logger.info).toHaveBeenCalledWith('Handled', { test: 'data' });
34
- });
35
-
36
- it('should handle invalid JSON', async () => {
37
- const message = { value: Buffer.from('invalid-json') };
38
- await consumer.onMessage({ message });
39
- expect(logger.error).toHaveBeenCalledWith(
40
- expect.stringContaining('[Kafka] Error processing message on topic test-topic:'),
41
- expect.anything()
42
- );
43
- });
44
-
45
- it('should skip empty messages', async () => {
46
- const message = { value: null };
47
- await consumer.onMessage({ message });
48
- expect(logger.info).not.toHaveBeenCalled();
49
- });
50
-
51
- it('should throw if topic, groupId or handle are not implemented', async () => {
52
- class IncompleteConsumer extends BaseConsumer {}
53
- const incomplete = new IncompleteConsumer();
54
- expect(() => incomplete.topic).toThrow("Property 'topic' must be implemented");
55
- expect(() => incomplete.groupId).toThrow("Property 'groupId' must be implemented");
56
- await expect(incomplete.handle({})).rejects.toThrow("Method 'handle()' must be implemented");
57
- });
58
- });
1
+ const BaseConsumer = require('<% if (architecture === "Clean Architecture") { %>@/interfaces/messaging/baseConsumer<% } else { %>@/messaging/baseConsumer<% } %>');
2
+ const logger = require('<% if (architecture === "Clean Architecture") { %>@/infrastructure/log/logger<% } else { %>@/utils/logger<% } %>');
3
+
4
+ jest.mock('<% if (architecture === "Clean Architecture") { %>@/infrastructure/log/logger<% } else { %>@/utils/logger<% } %>');
5
+
6
+ class TestConsumer extends BaseConsumer {
7
+ constructor() {
8
+ super();
9
+ this._topic = 'test-topic';
10
+ }
11
+ get topic() { return this._topic; }
12
+ get groupId() { return 'test-group'; }
13
+ async handle(data) {
14
+ logger.info('Handled', data);
15
+ }
16
+ }
17
+
18
+ describe('BaseConsumer', () => {
19
+ let consumer;
20
+
21
+ beforeEach(() => {
22
+ jest.clearAllMocks();
23
+ consumer = new TestConsumer();
24
+ });
25
+
26
+ it('should throw error if instantiated directly', () => {
27
+ expect(() => new BaseConsumer()).toThrow("Abstract class 'BaseConsumer' cannot be instantiated.");
28
+ });
29
+
30
+ it('should process a valid message', async () => {
31
+ const message = { value: Buffer.from(JSON.stringify({ test: 'data' })) };
32
+ await consumer.onMessage({ message });
33
+ expect(logger.info).toHaveBeenCalledWith('Handled', { test: 'data' });
34
+ });
35
+
36
+ it('should handle invalid JSON', async () => {
37
+ const message = { value: Buffer.from('invalid-json') };
38
+ await consumer.onMessage({ message });
39
+ expect(logger.error).toHaveBeenCalledWith(
40
+ expect.stringContaining('[Kafka] Error processing message on topic test-topic:'),
41
+ expect.anything()
42
+ );
43
+ });
44
+
45
+ it('should skip empty messages', async () => {
46
+ const message = { value: null };
47
+ await consumer.onMessage({ message });
48
+ expect(logger.info).not.toHaveBeenCalled();
49
+ });
50
+
51
+ it('should throw if topic, groupId or handle are not implemented', async () => {
52
+ class IncompleteConsumer extends BaseConsumer {}
53
+ const incomplete = new IncompleteConsumer();
54
+ expect(() => incomplete.topic).toThrow("Property 'topic' must be implemented");
55
+ expect(() => incomplete.groupId).toThrow("Property 'groupId' must be implemented");
56
+ await expect(incomplete.handle({})).rejects.toThrow("Method 'handle()' must be implemented");
57
+ });
58
+ });
@@ -1,11 +1,12 @@
1
- const { z } = require('zod');
2
-
3
- const UserEventSchema = z.object({
4
- action: z.enum(['USER_CREATED', 'UPDATE_USER', 'DELETE_USER']),
5
- payload: z.object({
6
- id: z.union([z.string(), z.number()]),
7
- email: z.string().email(),
8
- }),
9
- });
10
-
11
- module.exports = { UserEventSchema };
1
+ const { z } = require('zod');
2
+ const { KAFKA_ACTIONS } = require('<%- kafkaEventsPath %>');
3
+
4
+ const UserEventSchema = z.object({
5
+ action: z.enum(Object.values(KAFKA_ACTIONS)),
6
+ payload: z.object({
7
+ id: z.union([z.string(), z.number()]),
8
+ email: z.string().email().optional(),
9
+ }),
10
+ });
11
+
12
+ module.exports = { UserEventSchema };
@@ -1,27 +1,27 @@
1
- const { UserEventSchema } = require('<% if (architecture === "Clean Architecture") { %>@/interfaces/messaging/schemas/userEventSchema<% } else { %>@/messaging/schemas/userEventSchema<% } %>');
2
-
3
- describe('UserEventSchema', () => {
4
- it('should validate a correct USER_CREATED event', () => {
5
- const validEvent = {
6
- action: 'USER_CREATED',
7
- payload: {
8
- id: 1,
9
- email: 'test@example.com'
10
- }
11
- };
12
- const result = UserEventSchema.safeParse(validEvent);
13
- expect(result.success).toBe(true);
14
- });
15
-
16
- it('should fail validation for invalid email', () => {
17
- const invalidEvent = {
18
- action: 'USER_CREATED',
19
- payload: {
20
- id: 1,
21
- email: 'invalid-email'
22
- }
23
- };
24
- const result = UserEventSchema.safeParse(invalidEvent);
25
- expect(result.success).toBe(false);
26
- });
27
- });
1
+ const { UserEventSchema } = require('<% if (architecture === "Clean Architecture") { %>@/interfaces/messaging/schemas/userEventSchema<% } else { %>@/messaging/schemas/userEventSchema<% } %>');
2
+
3
+ describe('UserEventSchema', () => {
4
+ it('should validate a correct USER_CREATED event', () => {
5
+ const validEvent = {
6
+ action: 'USER_CREATED',
7
+ payload: {
8
+ id: 1,
9
+ email: 'test@example.com'
10
+ }
11
+ };
12
+ const result = UserEventSchema.safeParse(validEvent);
13
+ expect(result.success).toBe(true);
14
+ });
15
+
16
+ it('should fail validation for invalid email', () => {
17
+ const invalidEvent = {
18
+ action: 'USER_CREATED',
19
+ payload: {
20
+ id: 1,
21
+ email: 'invalid-email'
22
+ }
23
+ };
24
+ const result = UserEventSchema.safeParse(invalidEvent);
25
+ expect(result.success).toBe(false);
26
+ });
27
+ });
@@ -1,31 +1,44 @@
1
- const BaseConsumer = require('../../baseConsumer');
2
- const logger = require('<% if (architecture === "Clean Architecture") { %>../../../../infrastructure/log/logger<% } else { %>../../../utils/logger<% } %>');
3
- const { UserEventSchema } = require('../../schemas/userEventSchema');
4
-
5
- class WelcomeEmailConsumer extends BaseConsumer {
6
- constructor() {
7
- super();
8
- }
9
-
10
- get topic() { return 'user-topic'; }
11
- get groupId() { return 'welcome-email-group'; }
12
-
13
- async handle(data) {
14
- const result = UserEventSchema.safeParse(data);
15
-
16
- if (!result.success) {
17
- logger.error('[Kafka] Invalid user event data:', result.error.format());
18
- return;
19
- }
20
-
21
- const { action, payload } = result.data;
22
-
23
- if (action === 'USER_CREATED') {
24
- logger.info(`[Kafka] Consumer: Received USER_CREATED.`);
25
- logger.info(`[Kafka] Consumer: 📧 Sending welcome email to '${payload.email}'... Done!`);
26
- // In a real app, you would call an EmailService here
27
- }
28
- }
29
- }
30
-
31
- module.exports = WelcomeEmailConsumer;
1
+ const BaseConsumer = require('<%- baseConsumerPath %>');
2
+ const logger = require('<%- loggerPath %>');
3
+ const { UserEventSchema } = require('<%- userEventSchemaPath %>');
4
+ const ERROR_MESSAGES = require('<%- errorMessagesPath %>');
5
+ const { KAFKA_ACTIONS } = require('<%- kafkaEventsPath %>');
6
+
7
+ class WelcomeEmailConsumer extends BaseConsumer {
8
+ constructor() {
9
+ super();
10
+ }
11
+
12
+ get topic() { return 'user-topic'; }
13
+ get groupId() { return 'welcome-email-group'; }
14
+
15
+ async handle(data) {
16
+ const result = UserEventSchema.safeParse(data);
17
+
18
+ if (!result.success) {
19
+ logger.error(`[Kafka] ${ERROR_MESSAGES.INVALID_USER_DATA}:`, result.error.format());
20
+ return;
21
+ }
22
+
23
+ const { action, payload } = result.data;
24
+
25
+ switch (action) {
26
+ case KAFKA_ACTIONS.USER_CREATED:
27
+ logger.info(`[Kafka] Consumer: Received ${KAFKA_ACTIONS.USER_CREATED}.`);
28
+ logger.info(`[Kafka] Consumer: 📧 Sending welcome email to '${payload.email}'... Done!`);
29
+ break;
30
+ case KAFKA_ACTIONS.USER_UPDATED:
31
+ logger.info(`[Kafka] Consumer: Received ${KAFKA_ACTIONS.USER_UPDATED}.`);
32
+ logger.info(`[Kafka] Consumer: 🔄 Updating user records for '${payload.id}' (Email: ${payload.email})... Done!`);
33
+ break;
34
+ case KAFKA_ACTIONS.USER_DELETED:
35
+ logger.info(`[Kafka] Consumer: Received ${KAFKA_ACTIONS.USER_DELETED}.`);
36
+ logger.info(`[Kafka] Consumer: 🗑️ Cleaning up data for user '${payload.id}'... Done!`);
37
+ break;
38
+ default:
39
+ logger.warn(`[Kafka] Unknown action: ${action}`);
40
+ }
41
+ }
42
+ }
43
+
44
+ module.exports = WelcomeEmailConsumer;
@@ -1,49 +1,86 @@
1
- const WelcomeEmailConsumer = require('<% if (architecture === "Clean Architecture") { %>@/interfaces/messaging/consumers/instances/welcomeEmailConsumer<% } else { %>@/messaging/consumers/instances/welcomeEmailConsumer<% } %>');
2
- const logger = require('<% if (architecture === "Clean Architecture") { %>@/infrastructure/log/logger<% } else { %>@/utils/logger<% } %>');
3
-
4
- jest.mock('<% if (architecture === "Clean Architecture") { %>@/infrastructure/log/logger<% } else { %>@/utils/logger<% } %>');
5
-
6
- describe('WelcomeEmailConsumer', () => {
7
- let consumer;
8
-
9
- beforeEach(() => {
10
- jest.clearAllMocks();
11
- consumer = new WelcomeEmailConsumer();
12
- });
13
-
14
- it('should log welcome email simulation for USER_CREATED action', async () => {
15
- const data = {
16
- action: 'USER_CREATED',
17
- payload: {
18
- id: 1,
19
- email: 'test@example.com'
20
- }
21
- };
22
-
23
- await consumer.handle(data);
24
-
25
- expect(logger.info).toHaveBeenCalledWith(
26
- expect.stringContaining('[Kafka] Consumer: Received USER_CREATED.')
27
- );
28
- expect(logger.info).toHaveBeenCalledWith(
29
- expect.stringContaining('📧 Sending welcome email to \'test@example.com\'... Done!')
30
- );
31
- });
32
-
33
- it('should log error for invalid data', async () => {
34
- const data = {
35
- action: 'USER_CREATED',
36
- payload: {
37
- id: 1,
38
- email: 'invalid-email'
39
- }
40
- };
41
-
42
- await consumer.handle(data);
43
-
44
- expect(logger.error).toHaveBeenCalledWith(
45
- expect.stringContaining('[Kafka] Invalid user event data:'),
46
- expect.anything()
47
- );
48
- });
49
- });
1
+ const WelcomeEmailConsumer = require('<% if (architecture === "Clean Architecture") { %>@/interfaces/messaging/consumers/instances/welcomeEmailConsumer<% } else { %>@/messaging/consumers/instances/welcomeEmailConsumer<% } %>');
2
+ const logger = require('<% if (architecture === "Clean Architecture") { %>@/infrastructure/log/logger<% } else { %>@/utils/logger<% } %>');
3
+
4
+ jest.mock('<% if (architecture === "Clean Architecture") { %>@/infrastructure/log/logger<% } else { %>@/utils/logger<% } %>');
5
+
6
+ describe('WelcomeEmailConsumer', () => {
7
+ let consumer;
8
+
9
+ beforeEach(() => {
10
+ jest.clearAllMocks();
11
+ consumer = new WelcomeEmailConsumer();
12
+ });
13
+
14
+ it('should log welcome email simulation for USER_CREATED action', async () => {
15
+ const data = {
16
+ action: 'USER_CREATED',
17
+ payload: {
18
+ id: 1,
19
+ email: 'test@example.com'
20
+ }
21
+ };
22
+
23
+ await consumer.handle(data);
24
+
25
+ expect(logger.info).toHaveBeenCalledWith(
26
+ expect.stringContaining('[Kafka] Consumer: Received USER_CREATED.')
27
+ );
28
+ expect(logger.info).toHaveBeenCalledWith(
29
+ expect.stringContaining('📧 Sending welcome email to \'test@example.com\'... Done!')
30
+ );
31
+ });
32
+
33
+ it('should log update simulation for USER_UPDATED action', async () => {
34
+ const data = {
35
+ action: 'USER_UPDATED',
36
+ payload: {
37
+ id: 1,
38
+ email: 'updated@example.com'
39
+ }
40
+ };
41
+
42
+ await consumer.handle(data);
43
+
44
+ expect(logger.info).toHaveBeenCalledWith(
45
+ expect.stringContaining('[Kafka] Consumer: Received USER_UPDATED.')
46
+ );
47
+ expect(logger.info).toHaveBeenCalledWith(
48
+ expect.stringContaining('🔄 Updating user records for \'1\' (Email: updated@example.com)... Done!')
49
+ );
50
+ });
51
+
52
+ it('should log deletion simulation for USER_DELETED action', async () => {
53
+ const data = {
54
+ action: 'USER_DELETED',
55
+ payload: {
56
+ id: 1
57
+ }
58
+ };
59
+
60
+ await consumer.handle(data);
61
+
62
+ expect(logger.info).toHaveBeenCalledWith(
63
+ expect.stringContaining('[Kafka] Consumer: Received USER_DELETED.')
64
+ );
65
+ expect(logger.info).toHaveBeenCalledWith(
66
+ expect.stringContaining('🗑️ Cleaning up data for user \'1\'... Done!')
67
+ );
68
+ });
69
+
70
+ it('should log error for invalid data', async () => {
71
+ const data = {
72
+ action: 'USER_CREATED',
73
+ payload: {
74
+ id: 1,
75
+ email: 'invalid-email'
76
+ }
77
+ };
78
+
79
+ await consumer.handle(data);
80
+
81
+ expect(logger.error).toHaveBeenCalledWith(
82
+ expect.stringContaining('[Kafka] Invalid user event data:'),
83
+ expect.anything()
84
+ );
85
+ });
86
+ });
@@ -1,93 +1,93 @@
1
- const { kafka } = require('../config/kafka');
2
- const logger = require('<% if (architecture === "Clean Architecture") { %>../log/logger<% } else { %>../utils/logger<% } %>');
3
-
4
- let producer = null;
5
- let consumer = null;
6
- let isConnected = false;
7
- let connectionPromise = null;
8
-
9
- const connectKafka = async (retries = 10) => {
10
- if (connectionPromise) return connectionPromise;
11
-
12
- connectionPromise = (async () => {
13
- if (!producer) producer = kafka.producer();
14
- if (!consumer) consumer = kafka.consumer({ groupId: 'test-group' });
15
-
16
- let attempt = 0;
17
- // Auto-register WelcomeEmailConsumer if it exists
18
- // Note: Dynamic import used here for simplicity and to avoid startup crashes.
19
- // In enterprise production, consider using Dependency Injection.
20
- const WelcomeEmailConsumer = require('<% if (architecture === "Clean Architecture") { %>../../interfaces/messaging/consumers/instances/welcomeEmailConsumer<% } else { %>../messaging/consumers/instances/welcomeEmailConsumer<% } %>');
21
- while (attempt < retries) {
22
- try {
23
- await producer.connect();
24
- await consumer.connect();
25
- logger.info('[Kafka] Producer connected successfully');
26
- logger.info('[Kafka] Consumer connected successfully');
27
- isConnected = true;
28
-
29
- try {
30
- const welcomeConsumer = new WelcomeEmailConsumer();
31
- await consumer.subscribe({ topic: welcomeConsumer.topic, fromBeginning: true });
32
- logger.info(`[Kafka] Registered consumer for topic: ${welcomeConsumer.topic}`);
33
-
34
- await consumer.run({
35
- eachMessage: async (payload) => welcomeConsumer.onMessage(payload),
36
- });
37
- } catch (error) {
38
- // Fallback or no consumers found
39
- await consumer.subscribe({ topic: 'user-topic', fromBeginning: true });
40
- await consumer.run({
41
- eachMessage: async ({ message }) => {
42
- logger.info({ value: message.value.toString() });
43
- },
44
- });
45
- }
46
- return; // Success
47
- } catch (error) {
48
- attempt++;
49
- logger.error(`[Kafka] Connection attempt ${attempt} failed:`, error.message);
50
- if (attempt >= retries) {
51
- throw error; // Rethrow after final attempt
52
- }
53
- await new Promise(res => setTimeout(res, 3000)); // Wait 3s between retries
54
- }
55
- }
56
- })();
57
-
58
- return connectionPromise;
59
- };
60
-
61
- const sendMessage = async (topic, message) => {
62
- if (connectionPromise) {
63
- await connectionPromise;
64
- }
65
-
66
- if (!isConnected) {
67
- throw new Error('[Kafka] Producer not connected. Check logs for connection errors.');
68
- }
69
-
70
- try {
71
- await producer.send({
72
- topic,
73
- messages: [{ value: message }],
74
- });
75
-
76
- try {
77
- const parsed = JSON.parse(message);
78
- logger.info(`[Kafka] Producer: Sent ${parsed.action} event for '${parsed.payload?.email || 'unknown'}'`);
79
- } catch (error) {
80
- logger.info(`[Kafka] Producer: Sent message to ${topic}`, error);
81
- }
82
- } catch (error) {
83
- logger.error(`[Kafka] Failed to send message to ${topic}:`, error.message);
84
- throw error;
85
- }
86
- };
87
-
88
- const disconnectKafka = async () => {
89
- if (producer) await producer.disconnect();
90
- if (consumer) await consumer.disconnect();
91
- };
92
-
93
- module.exports = { connectKafka, sendMessage, disconnectKafka };
1
+ const { kafka } = require('../config/kafka');
2
+ const logger = require('<% if (architecture === "Clean Architecture") { %>../log/logger<% } else { %>../utils/logger<% } %>');
3
+
4
+ let producer = null;
5
+ let consumer = null;
6
+ let isConnected = false;
7
+ let connectionPromise = null;
8
+
9
+ const connectKafka = async (retries = 10) => {
10
+ if (connectionPromise) return connectionPromise;
11
+
12
+ connectionPromise = (async () => {
13
+ if (!producer) producer = kafka.producer();
14
+ if (!consumer) consumer = kafka.consumer({ groupId: 'test-group' });
15
+
16
+ let attempt = 0;
17
+ // Auto-register WelcomeEmailConsumer if it exists
18
+ // Note: Dynamic import used here for simplicity and to avoid startup crashes.
19
+ // In enterprise production, consider using Dependency Injection.
20
+ const WelcomeEmailConsumer = require('<% if (architecture === "Clean Architecture") { %>../../interfaces/messaging/consumers/instances/welcomeEmailConsumer<% } else { %>../messaging/consumers/instances/welcomeEmailConsumer<% } %>');
21
+ while (attempt < retries) {
22
+ try {
23
+ await producer.connect();
24
+ await consumer.connect();
25
+ logger.info('[Kafka] Producer connected successfully');
26
+ logger.info('[Kafka] Consumer connected successfully');
27
+ isConnected = true;
28
+
29
+ try {
30
+ const welcomeConsumer = new WelcomeEmailConsumer();
31
+ await consumer.subscribe({ topic: welcomeConsumer.topic, fromBeginning: true });
32
+ logger.info(`[Kafka] Registered consumer for topic: ${welcomeConsumer.topic}`);
33
+
34
+ await consumer.run({
35
+ eachMessage: async (payload) => welcomeConsumer.onMessage(payload),
36
+ });
37
+ } catch (error) {
38
+ // Fallback or no consumers found
39
+ await consumer.subscribe({ topic: 'user-topic', fromBeginning: true });
40
+ await consumer.run({
41
+ eachMessage: async ({ message }) => {
42
+ logger.info({ value: message.value.toString() });
43
+ },
44
+ });
45
+ }
46
+ return; // Success
47
+ } catch (error) {
48
+ attempt++;
49
+ logger.error(`[Kafka] Connection attempt ${attempt} failed:`, error.message);
50
+ if (attempt >= retries) {
51
+ throw error; // Rethrow after final attempt
52
+ }
53
+ await new Promise(res => setTimeout(res, 3000)); // Wait 3s between retries
54
+ }
55
+ }
56
+ })();
57
+
58
+ return connectionPromise;
59
+ };
60
+
61
+ const sendMessage = async (topic, message, key) => {
62
+ if (connectionPromise) {
63
+ await connectionPromise;
64
+ }
65
+
66
+ if (!isConnected) {
67
+ throw new Error('[Kafka] Producer not connected. Check logs for connection errors.');
68
+ }
69
+
70
+ try {
71
+ await producer.send({
72
+ topic,
73
+ messages: [{ key, value: message }],
74
+ });
75
+
76
+ try {
77
+ const parsed = JSON.parse(message);
78
+ logger.info(`[Kafka] Producer: Sent ${parsed.action} event for '${parsed.payload?.email || 'unknown'}'`);
79
+ } catch (error) {
80
+ logger.info(`[Kafka] Producer: Sent message to ${topic}`, error);
81
+ }
82
+ } catch (error) {
83
+ logger.error(`[Kafka] Failed to send message to ${topic}:`, error.message);
84
+ throw error;
85
+ }
86
+ };
87
+
88
+ const disconnectKafka = async () => {
89
+ if (producer) await producer.disconnect();
90
+ if (consumer) await consumer.disconnect();
91
+ };
92
+
93
+ module.exports = { connectKafka, sendMessage, disconnectKafka };